Skip to content

feat(walletui): send flow ux improvements - #1570

Merged
sdbondi merged 9 commits into
tari-project:developmentfrom
NovaT82:wallet-ui
Sep 11, 2025
Merged

feat(walletui): send flow ux improvements#1570
sdbondi merged 9 commits into
tari-project:developmentfrom
NovaT82:wallet-ui

Conversation

@NovaT82

@NovaT82 NovaT82 commented Sep 10, 2025

Copy link
Copy Markdown
Contributor

Description

Split the send flow into 3 steps: form, confirmation and result.
Added some ux improvements, like allowing the user to type in whole amounts, instead of typing in minotari.
Added a field that shows available balance, underneath the amount field and an error if you exceed the available amount.
The fee gets calculated automatically when the amount has been added.

Screenshot 2025-09-10 at 09 11 34 Screenshot 2025-09-10 at 09 12 23 Screenshot 2025-09-10 at 09 12 32 Screenshot 2025-09-10 at 09 12 39

Motivation and Context

Improves the send flow and make it a bit more user friendly

How Has This Been Tested?

Manually

What process can a PR reviewer use to test or verify this change?

In the assets section, click on "Send" next to the asset and follow the steps

Breaking Changes

x

  • None
  • Requires data directory to be deleted
  • Other - Please specify

Summary by CodeRabbit

  • New Features

    • Tokens view added: tabular balances with per-token Send action.
    • New multi-step Send dialog with fee estimation, badge support, and success/failure feedback.
    • Dynamic token symbols; currency symbol updated to tXTR; amounts use locale-aware formatting.
    • NFT pagination now shows accurate totals; NFT fee displays use the new formatter.
  • Refactor

    • Send flow moved from Asset Vault balances into the Tokens view.
  • Chores

    • Standardized component import paths via aliases.

@coderabbitai

coderabbitai Bot commented Sep 10, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Introduces a new Tokens feature and Send Money flow under AssetVault, replacing the old inline assets/balance and send UI. Updates currency helpers (formatXTM→formatCurrency), changes currency symbol to tXTR, and switches many component imports to the @components alias. Adjusts config aliases. Minor NFT steps formatting updates and dynamic token symbol in AccountBalance.

Changes

Cohort / File(s) Summary
Path alias updates
applications/tari_walletd/web_ui/src/routes/Accounts/Accounts.tsx, .../FlowEditor/FlowEditor.tsx, .../Keys/Keys.tsx, .../Templates/Templates.tsx, .../Wallet/Components/AccessTokens.tsx, .../WebauthnRegistration/Components/Login.tsx, .../WebauthnRegistration/Components/Registration.tsx, .../WebauthnRegistration/Webauthn.tsx
Switched various component imports from relative paths to @components/.... No logic changes.
Config alias changes
applications/tari_walletd/web_ui/tsconfig.json, applications/tari_walletd/web_ui/vite.config.ts
Updated @components path alias from ./src/Components to ./src/components.
Tokens feature (new)
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx, .../Tokens/components/SendMoney.tsx, .../Tokens/steps/FormStep.tsx, .../Tokens/steps/ConfirmationStep.tsx, .../Tokens/steps/ResultStep.tsx
Added Tokens table view with Send flow: form, confirmation, result; integrates balances, fee estimation via dry-run, and transfer execution.
Assets refactor to Tokens
applications/tari_walletd/web_ui/src/routes/AssetVault/Components/Assets.tsx
Removed inline token/balance table and send logic; delegates token rendering to <Tokens />. NFT pagination uses actual total when available.
Legacy send removed
applications/tari_walletd/web_ui/src/routes/AssetVault/Components/SendMoney.tsx
Deleted previous Send Money module (button, dialog, form, transfer/fee logic).
Action menu wiring
applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ActionMenu.tsx
Updated SendMoney import to new Tokens location.
Dynamic token symbol in balances
applications/tari_walletd/web_ui/src/routes/AssetVault/Components/AccountBalance.tsx
Replaced static currency symbol with dynamic token symbol derived from balances. Minor refactor.
NFT steps formatting helper
applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/steps/ConfirmationStep.tsx, .../NFTs/steps/FormStep.tsx
Switched fee formatting from formatXTM to formatCurrency.
Currency helpers and constants
applications/tari_walletd/web_ui/src/utils/helpers.tsx, applications/tari_walletd/web_ui/src/utils/constants.ts
Renamed formatXTMformatCurrency; added formatDisplayCurrency; made formatting locale-aware; changed CURRENCY.SYMBOL from "tXTM" to "tXTR".

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Tokens as Tokens View
  participant Dialog as SendMoneyDialog
  participant Form as FormStep
  participant Confirm as ConfirmationStep
  participant Result as ResultStep
  participant API as Wallet APIs
  note over Tokens: Renders balances via useAccountsGetBalances
  User->>Tokens: Click "Send" on a token row
  Tokens->>Dialog: Open with resource info (addr/type/symbol/divisibility)
  Dialog->>Form: Show transfer form
  Form-->>Dialog: onChange(publicKey/amount/selection)
  alt Auto fee estimation conditions met
    Dialog->>API: Transfer(dry_run: true)
    API-->>Dialog: Dry-run tx id
    Dialog->>API: transactionsWaitResult(id)
    API-->>Dialog: Estimated fee
    Dialog-->>Form: Update fee field
  end
  User->>Confirm: Continue to confirmation
  Confirm->>Dialog: onConfirm()
  Dialog->>API: Transfer(dry_run: false, max_fee)
  API-->>Dialog: Success/Failure
  Dialog->>Result: Show outcome
  Result-->>Tokens: onClose (and on success, onSendComplete)
Loading
sequenceDiagram
  autonumber
  participant Assets as Assets.tsx
  participant Tokens as Tokens.tsx
  note over Assets: Previous inline balances & send removed
  Assets->>Tokens: Render <Tokens account={...}/>
  Tokens->>Tokens: Fetch balances, render table
  Tokens->>Tokens: Handle Send dialog lifecycle
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

CR-too_long

Suggested reviewers

  • sdbondi

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “feat(walletui): send flow ux improvements” clearly captures the primary change of enhancing the send flow user experience in the wallet UI without extraneous details or vague language. It directly reflects the main objective of splitting the send flow into form, confirmation, and result steps and improving amount entry and fee calculation. Because it succinctly summarizes the key UX improvements for this feature, it aligns well with the PR content.

Poem

A hop, a skip, new Tokens bloom—
I nudge the fees, then whoosh, zoom-zoom!
Old warren cleared, new trails align,
tXTR shines, the numbers fine.
With gentle taps, I send with glee—
Carrots balanced, bug-free spree! 🥕✨

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

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

Caution

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

⚠️ Outside diff range comments (3)
applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Components/Registration.tsx (1)

181-184: Add rel="noopener noreferrer" to external link

Prevents reverse tabnabbing when using target="_blank".

-              <a href="https://webauthn.io/" target="_blank">
+              <a href="https://webauthn.io/" target="_blank" rel="noopener noreferrer">
applications/tari_walletd/web_ui/src/routes/AssetVault/Components/Assets.tsx (1)

46-51: Fix TabPanel id/aria-controls mismatch (a11y bug).

Tabs use asset-tab*/asset-tabpanel* while TabPanel renders simple-tab*/simple-tabpanel*. Screen readers and keyboard focus will get inconsistent associations.

Apply:

-      id={`simple-tabpanel-${index}`}
-      aria-labelledby={`simple-tab-${index}`}
+      id={`asset-tabpanel-${index}`}
+      aria-labelledby={`asset-tab-${index}`}

Also applies to: 62-66

applications/tari_walletd/web_ui/src/utils/helpers.tsx (1)

253-264: BigInt→Number conversion can lose precision for large amounts.

Number(integerPart) may overflow >2^53-1. Use bigintToDecimalString to format safely and consistently.

Apply:

 export const formatCurrency = (amount: number | bigint): string => {
   if (typeof amount === "bigint") {
-    // Handle bigint: divide by divisor to get integer and remainder for fractional part
-    const divisor = BigInt(CURRENCY.DIVISOR);
-    const integerPart = amount / divisor;
-    const remainder = amount % divisor;
-
-    // Convert remainder to fractional string padded to CURRENCY.DECIMALS
-    const fractionalPart = remainder.toString().padStart(CURRENCY.DECIMALS, "0");
-
-    return `${Number(integerPart).toLocaleString("en-US")}.${fractionalPart} ${CURRENCY.SYMBOL}`;
+    const dec = bigintToDecimalString(amount, CURRENCY.DECIMALS, "en-US");
+    // Trim trailing zeros to align with number-branch behavior
+    const trimmed = dec.replace(/(\.\d*?[1-9])0+$/,"$1").replace(/\.0+$/,"");
+    return `${trimmed} ${CURRENCY.SYMBOL}`;
   } else if (typeof amount === "number") {
🧹 Nitpick comments (26)
applications/tari_walletd/web_ui/src/routes/AssetVault/Components/AccountBalance.tsx (4)

90-93: Hook up loading state to FetchStatusCheck

You're forcing isLoading to false, so the wrapper never shows its loading UI. Pass through balancesIsLoading.

-    <FetchStatusCheck
-      isError={balancesIsError}
-      errorMessage={balancesError?.message || "Error fetching data"}
-      isLoading={false}
-    >
+    <FetchStatusCheck
+      isError={balancesIsError}
+      errorMessage={balancesError?.message || "Error fetching data"}
+      isLoading={balancesIsLoading}
+    >

34-35: Fallback symbol + hide when masked

  • Provide a safe fallback to the configured currency symbol.
  • Avoid leaking the symbol when the balance is hidden.
-// import { CURRENCY } from "@utils/constants";
+import { CURRENCY } from "@utils/constants";
@@
-  const symbol = balancesData?.balances.find((b) => b.resource_address === XTR_RESOURCE)?.token_symbol || "";
+  const symbol =
+    balancesData?.balances.find((b) => b.resource_address === XTR_RESOURCE)?.token_symbol || CURRENCY.SYMBOL;
@@
-            <Typography variant="h2">
-              {formattedBalance} <span style={{ fontSize: "18px" }}>{symbol}</span>
+            <Typography variant="h2">
+              {formattedBalance} <span style={{ fontSize: "18px" }}>{showBalance ? symbol : ""}</span>

Also applies to: 86-86, 110-112


68-70: Effect dependency should not be the whole account object

Using account as a dependency can refire on identity changes. Depend on a stable key (address).

-  useEffect(() => {
-    refetch();
-  }, [account, refetch]);
+  useEffect(() => {
+    refetch();
+  }, [account.address, refetch]);

79-80: Use configured decimals instead of magic number

Prefer CURRENCY.DECIMALS as the default.

-      const xtr_decimals = balanceObj?.divisibility || 6;
+      const xtr_decimals = balanceObj?.divisibility ?? CURRENCY.DECIMALS;
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/ResultStep.tsx (2)

41-48: Auto-close only on success

Auto-closing failures hides error context. Close automatically on success; keep failures open until the user acknowledges.

-    if (!disabled && transferResult) {
+    if (!disabled && transferResult?.success) {
       const timer = setTimeout(() => {
         onClose();
       }, 10000);

51-58: Improve accessibility for the sending state

Announce progress to assistive tech.

-    <Stack direction="column" spacing={2} alignItems="center" justifyContent="center" pt={3}>
+    <Stack direction="column" spacing={2} alignItems="center" justifyContent="center" pt={3} aria-live="polite">
       {disabled ? (
         <>
-          <CircularProgress size={60} />
+          <CircularProgress size={60} aria-label="Transaction in progress" />
           <Typography variant="h6">Sending Money...</Typography>
applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/steps/ConfirmationStep.tsx (1)

129-130: Avoid parseInt; use BigInt for fees

Prevents NaN/precision issues and aligns with helpers that accept bigint.

-              <Typography>{formatCurrency(parseInt(transferFormState.maxFee))}</Typography>
+              <Typography>{formatCurrency(BigInt(transferFormState.maxFee || "0"))}</Typography>
applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/steps/FormStep.tsx (3)

133-138: Prefer BigInt over parseInt for fee display

Avoids NaN/precision pitfalls; helper supports bigint.

-              : transferFormState.maxFee
-                ? formatCurrency(parseInt(transferFormState.maxFee))
+              : transferFormState.maxFee
+                ? formatCurrency(BigInt(transferFormState.maxFee || "0"))
                 : "Will be calculated automatically"

109-114: Stabilize React key

Account names may collide; use address.

-              {accounts.map((account) => (
-                <MenuItem key={account.account.name} value={substateIdToString(account.account.address)}>
+              {accounts.map((account) => (
+                <MenuItem key={substateIdToString(account.account.address)} value={substateIdToString(account.account.address)}>

156-157: Simplify renderValue

map(identity) is redundant.

-              renderValue={(selected) => selected.map((item) => item).join(", ")}
+              renderValue={(selected) => selected.join(", ")}
applications/tari_walletd/web_ui/src/routes/AssetVault/Components/Assets.tsx (1)

87-98: Avoid fetching the full NFT list just to compute totalCount.

useListNfts likely pulls all items, which won’t scale. Prefer a paginated endpoint that returns total, or extend useAccountNFTsList to include total.

If the hook exposes total, simplify:

-  const { data: allNfts } = useListNfts({
-    account: { ComponentAddress: substateIdToString(account.address) },
-  });
-  const actualTotal = allNfts ? allNfts.length : null;
-  const totalCount = actualTotal !== null ? actualTotal : currentNfts.length;
+  const totalCount = nftsListData?.total ?? currentNfts.length;
applications/tari_walletd/web_ui/src/utils/helpers.tsx (1)

269-271: Locale handling: prefer environment locale or make it configurable.

Hard-coding "en-US" may surprise non-US users.

-    return `${convertedAmount.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: CURRENCY.DECIMALS })} ${CURRENCY.SYMBOL}`;
+    return `${convertedAmount.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: CURRENCY.DECIMALS })} ${CURRENCY.SYMBOL}`;
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/ConfirmationStep.tsx (3)

23-28: Import the formatter you actually use; drop unused bigint import.

Prevents lint noise and clarifies intent.

-import { formatCurrency, bigintToDecimalString } from "@utils/helpers";
+import { formatCurrency, formatDisplayCurrency } from "@utils/helpers";

62-66: Format the confirmation amount in display units and use the provided symbol.

Shows thousand separators and respects decimals; aligns with the new “whole units” UX.

-          <Typography variant="body1">
-            {transferFormState.amount}
-            {token_symbol ? ` ${token_symbol}` : ""}
-          </Typography>
+          <Typography variant="body1">
+            {formatDisplayCurrency(Number(transferFormState.amount))}
+            {token_symbol ? ` ${token_symbol}` : ""}
+          </Typography>

110-118: Optional: show a “Total to be deducted” row (amount + fee).

Reduces surprises for users.

If desired, compute total in parent and pass it down to display here.

applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/FormStep.tsx (3)

214-216: Minor UX nits.

  • The button label condition is redundant.
  • Set a clearer fee placeholder.
-          InputProps={{
-            placeholder: "0.0",
+          InputProps={{
+            placeholder: "0.0",
             endAdornment: token_symbol ? <InputAdornment position="end">{token_symbol}</InputAdornment> : undefined,
           }}
...
-          <Button variant="contained" type="submit" disabled={disabled || !isFormValid}>
-            {isEstimatingFee ? "Estimating..." : transferFormState.fee ? "Continue" : "Continue"}
+          <Button variant="contained" type="submit" disabled={disabled || !isFormValid}>
+            {isEstimatingFee ? "Estimating..." : "Continue"}
           </Button>

Also applies to: 241-245


125-151: Show “Use Badge” only when badges exist.

-{badges && (
+{Array.isArray(badges) && badges.length > 0 && (
   <>

153-163: Constrain key input length to 64 hex chars.

-          <TextField
+          <TextField
             name="publicKey"
             label="To Public Key"
             value={transferFormState.publicKey}
-            inputProps={{ pattern: "^[0-9a-fA-F]*$" }}
+            inputProps={{ pattern: "^[0-9a-fA-F]*$", minLength: 64, maxLength: 64 }}
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx (3)

175-177: Prefer stable keys.

Use resource_address rather than array index.

-                    key={i}
+                    key={resource_address}

58-64: Type consistency for resourceType.

-interface ConfidentialBalanceProps {
+interface ConfidentialBalanceProps {
   show: boolean;
   balance: Amount;
-  resourceType: string;
+  resourceType: ResourceType;
   divisibility: number;
   token_symbol?: string;
 }

Also applies to: 66-74


97-98: Fallback symbol for revealed balance display.

-        {showBalance ? bigintToDecimalString(balance, divisibility) + " " + token_symbol : "*************"}
+        {showBalance ? bigintToDecimalString(balance, divisibility) + " " + (token_symbol || CURRENCY.SYMBOL) : "*************"}

(Remember the earlier CURRENCY import.)

applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx (5)

373-378: Dialog title should reflect the asset being sent.

-          <Typography variant="h4">Send Tari</Typography>
+          <Typography variant="h4">Send {props.token_symbol || "Tokens"}</Typography>

114-141: Avoid precision/overflow by staying in base units.

Number(revealedBalance) can overflow for large balances. Consider returning available balance in base units (BigInt) and compare against the user-entered amount converted to base units before display.

I can provide a BigInt-based helper and wire it through FormStep if you’d like.


98-99: Remove unused variable.

-  const { account, setPopup } = useAccountStore();
+  const { account } = useAccountStore();

205-233: Hard-coded fee headroom and max_fee.

3000 and +100 are magic numbers. Consider centralizing in constants or using the hook’s default max fee throughout for consistency.


315-328: Auto-estimate regex allows any length hex; align with validateAddress (64 chars).

-    if (publicKey.trim() && publicKey.match(/^[0-9a-fA-F]+$/) && amount.trim()) {
+    if (publicKey.trim() && /^[0-9a-fA-F]{64}$/.test(publicKey) && amount.trim()) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8952029 and 7120e7e.

📒 Files selected for processing (23)
  • applications/tari_walletd/web_ui/src/routes/Accounts/Accounts.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/Components/AccountBalance.tsx (4 hunks)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ActionMenu.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/Components/Assets.tsx (2 hunks)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/Components/SendMoney.tsx (0 hunks)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/steps/ConfirmationStep.tsx (2 hunks)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/steps/FormStep.tsx (2 hunks)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/ConfirmationStep.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/FormStep.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/ResultStep.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/routes/FlowEditor/FlowEditor.tsx (3 hunks)
  • applications/tari_walletd/web_ui/src/routes/Keys/Keys.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/routes/Templates/Templates.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/routes/Wallet/Components/AccessTokens.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Components/Login.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Components/Registration.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Webauthn.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/utils/constants.ts (1 hunks)
  • applications/tari_walletd/web_ui/src/utils/helpers.tsx (3 hunks)
  • applications/tari_walletd/web_ui/tsconfig.json (1 hunks)
  • applications/tari_walletd/web_ui/vite.config.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/Components/SendMoney.tsx
🧰 Additional context used
🧬 Code graph analysis (8)
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/ConfirmationStep.tsx (5)
bindings/src/types/ResourceAddress.ts (1)
  • ResourceAddress (6-6)
bindings/src/types/ResourceType.ts (1)
  • ResourceType (20-20)
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/FormStep.tsx (1)
  • SendMoneyFormState (37-44)
applications/tari_walletd/web_ui/src/utils/helpers.tsx (1)
  • formatCurrency (253-275)
applications/tari_walletd/web_ui/src/Components/CopyAddress.tsx (1)
  • CopyAddress (32-43)
applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/steps/ConfirmationStep.tsx (1)
applications/tari_walletd/web_ui/src/utils/helpers.tsx (1)
  • formatCurrency (253-275)
applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/steps/FormStep.tsx (1)
applications/tari_walletd/web_ui/src/utils/helpers.tsx (1)
  • formatCurrency (253-275)
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx (10)
applications/tari_walletd/web_ui/src/api/hooks/useAccounts.ts (2)
  • useAccountsGetBalances (226-239)
  • useAccountsTransfer (103-156)
applications/tari_walletd/web_ui/src/utils/helpers.tsx (1)
  • substateIdToString (98-107)
bindings/src/helpers/consts.ts (1)
  • XTR (10-10)
bindings/src/types/ResourceAddress.ts (1)
  • ResourceAddress (6-6)
bindings/src/types/ResourceType.ts (1)
  • ResourceType (20-20)
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/FormStep.tsx (2)
  • SendMoneyFormState (37-44)
  • FormStep (65-250)
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/ResultStep.tsx (2)
  • TransferResult (28-31)
  • ResultStep (39-81)
bindings/src/types/wallet-daemon-client/BalanceEntry.ts (1)
  • BalanceEntry (7-15)
bindings/src/types/ConfidentialTransferInputSelection.ts (1)
  • ConfidentialTransferInputSelection (3-7)
applications/tari_walletd/web_ui/src/utils/json_rpc.ts (1)
  • transactionsWaitResult (257-258)
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/FormStep.tsx (4)
bindings/src/types/ResourceAddress.ts (1)
  • ResourceAddress (6-6)
bindings/src/types/ResourceType.ts (1)
  • ResourceType (20-20)
applications/tari_walletd/web_ui/src/utils/helpers.tsx (2)
  • validateAddress (290-301)
  • formatDisplayCurrency (278-283)
applications/tari_walletd/web_ui/src/utils/constants.ts (1)
  • CURRENCY (23-27)
applications/tari_walletd/web_ui/src/routes/AssetVault/Components/AccountBalance.tsx (1)
bindings/src/types/Account.ts (1)
  • Account (4-10)
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx (10)
bindings/src/types/ResourceAddress.ts (1)
  • ResourceAddress (6-6)
bindings/src/types/ResourceType.ts (1)
  • ResourceType (20-20)
bindings/src/types/VaultId.ts (1)
  • VaultId (6-6)
bindings/src/types/Amount.ts (1)
  • Amount (12-12)
applications/tari_walletd/web_ui/src/utils/helpers.tsx (1)
  • bigintToDecimalString (231-251)
applications/tari_walletd/web_ui/src/Components/CopyAddress.tsx (1)
  • CopyAddress (32-43)
bindings/src/types/Account.ts (1)
  • Account (4-10)
applications/tari_walletd/web_ui/src/api/hooks/useAccounts.ts (1)
  • useAccountsGetBalances (226-239)
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx (1)
  • SendMoneyDialog (80-389)
bindings/src/types/wallet-daemon-client/BalanceEntry.ts (1)
  • BalanceEntry (7-15)
applications/tari_walletd/web_ui/src/utils/helpers.tsx (1)
applications/tari_walletd/web_ui/src/utils/constants.ts (1)
  • CURRENCY (23-27)
🪛 Biome (2.1.2)
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx

[error] 104-104: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 316-316: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test
  • GitHub Check: check nightly
  • GitHub Check: clippy
🔇 Additional comments (14)
applications/tari_walletd/web_ui/src/routes/Templates/Templates.tsx (1)

26-27: Import alias migration is consistent with config.

No functional changes; resolves through @components as intended.

applications/tari_walletd/web_ui/src/routes/Accounts/Accounts.tsx (1)

23-25: Switched to @components aliases — good cleanup.

Keeps imports stable across moves/renames.

applications/tari_walletd/web_ui/src/routes/Wallet/Components/AccessTokens.tsx (1)

47-53: Alias-based imports LGTM.

Barrel import from StyledComponents and CopyAddress/FetchStatusCheck align with the new path strategy.

applications/tari_walletd/web_ui/src/routes/FlowEditor/FlowEditor.tsx (1)

24-24: Alias updates for UI components are correct.

Matches vite/tsconfig mappings; no behavioral impact.

Also applies to: 27-27, 55-55, 75-75

applications/tari_walletd/web_ui/tsconfig.json (1)

28-28: Path mapping to ./src/components/ is correct and matches Vite.*

Restart IDE/TS server after merging to avoid stale resolution.

applications/tari_walletd/web_ui/src/utils/constants.ts (1)

24-24: No stale tXTM references remain
Ran searches for tXTM and formatXTM as well as all uses of CURRENCY.SYMBOL in applications/tari_walletd/web_ui/src—no matches found.

applications/tari_walletd/web_ui/src/routes/Keys/Keys.tsx (1)

23-25: Alias imports look good.

Consistent with the project-wide alias migration.

applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ActionMenu.tsx (1)

29-29: Import path update looks good

SendMoney relocation aligns with the new Tokens flow. No issues spotted.

applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Webauthn.tsx (1)

7-7: Alias import LGTM

Switch to @components/Loading is consistent with the repo-wide aliasing.

applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Components/Login.tsx (1)

7-7: Alias import LGTM

Loading import via @components is correct.

applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Components/Registration.tsx (1)

18-22: Signature reformat LGTM

No behavioral changes; readability improved.

applications/tari_walletd/web_ui/src/routes/AssetVault/Components/Assets.tsx (1)

112-112: Delegation to Tokens looks good.

Props are typed and the account change reset logic aligns with the new split flow.

applications/tari_walletd/web_ui/src/utils/helpers.tsx (1)

277-283: New display-unit formatter looks good.

Consistent with DECIMALS and SYMBOL, and guards NaN.

applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/ConfirmationStep.tsx (1)

36-38: Unused prop: divisibility.

Either use it to gate displayed decimals or remove it from props.

Do you intend to format amount based on divisibility here, or can we drop the prop?

Comment on lines +100 to +104
if (!account) {
return null;
}

const { data } = useAccountsGetBalances(substateIdToString(account.address));

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.

⚠️ Potential issue

Hooks order violation due to early return before calling hooks.

Returning before later hooks causes inconsistent hook order.

Apply:

-  if (!account) {
-    return null;
-  }
-
-  const { data } = useAccountsGetBalances(substateIdToString(account.address));
+  const { data } = useAccountsGetBalances(substateIdToString(account.address));
...
+  // Keep hooks above; bail out after hooks to satisfy rules-of-hooks
+  if (!account) {
+    return null;
+  }

Also applies to: 316-328

🧰 Tools
🪛 Biome (2.1.2)

[error] 104-104: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

🤖 Prompt for AI Agents
In
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx
around lines 100-104 (and similarly at 316-328), there is an early return when
account is falsy before subsequent hooks are invoked, which breaks React hook
ordering; fix by moving any hook calls (e.g., useAccountsGetBalances and any
other hooks used later) above the conditional return so hooks are always called
in the same order, then guard rendering/logic using the account value (e.g.,
return null or a loading state) after hooks have been called; ensure the same
change is applied to the block at lines 316-328.

Comment on lines +225 to +235
const result = await sendIt?.({ ...currentTransfer, dry_run: true, max_fee: 3000 });
const resp = await transactionsWaitResult({ transaction_id: result.transaction_id, timeout_secs: null });
const transactionResult = resp.result?.result;

if (!transactionResult || !("Accept" in transactionResult)) {
throw new Error("Fee estimation failed");
}

const fee = resp.final_fee + 100;
setTransferFormState((prevState) => ({ ...prevState, fee: fee.toString() }));
} catch (error) {

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.

⚠️ Potential issue

Potential undefined ‘result’ due to optional sendIt; then property access.

sendIt is defined; optional chaining can make result undefined and crash on transaction_id.

-      const result = await sendIt?.({ ...currentTransfer, dry_run: true, max_fee: 3000 });
+      const result = await sendIt({ ...currentTransfer, dry_run: true, max_fee: 3000 });
       const resp = await transactionsWaitResult({ transaction_id: result.transaction_id, timeout_secs: null });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const result = await sendIt?.({ ...currentTransfer, dry_run: true, max_fee: 3000 });
const resp = await transactionsWaitResult({ transaction_id: result.transaction_id, timeout_secs: null });
const transactionResult = resp.result?.result;
if (!transactionResult || !("Accept" in transactionResult)) {
throw new Error("Fee estimation failed");
}
const fee = resp.final_fee + 100;
setTransferFormState((prevState) => ({ ...prevState, fee: fee.toString() }));
} catch (error) {
const result = await sendIt({ ...currentTransfer, dry_run: true, max_fee: 3000 });
const resp = await transactionsWaitResult({ transaction_id: result.transaction_id, timeout_secs: null });
const transactionResult = resp.result?.result;
if (!transactionResult || !("Accept" in transactionResult)) {
throw new Error("Fee estimation failed");
}
const fee = resp.final_fee + 100;
setTransferFormState((prevState) => ({ ...prevState, fee: fee.toString() }));
} catch (error) {
🤖 Prompt for AI Agents
In
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx
around lines 225-235, the call uses optional chaining on sendIt so result can be
undefined and subsequent access to result.transaction_id will throw; ensure
sendIt is present before calling it (e.g., if (!sendIt) throw new Error("sendIt
not available")), call sendIt without optional chaining to get a defined result,
then validate result and result.transaction_id (throw a clear error if missing)
before passing it to transactionsWaitResult; update the surrounding error
handling/types accordingly.

Comment on lines +276 to +281
await sendIt?.({
...transfer,
dry_run: false,
max_fee: parseInt(transferFormState.fee),
});

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.

⚠️ Potential issue

Same issue in confirm path; drop optional chaining.

-      await sendIt?.({
+      await sendIt({
         ...transfer,
         dry_run: false,
         max_fee: parseInt(transferFormState.fee),
       });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await sendIt?.({
...transfer,
dry_run: false,
max_fee: parseInt(transferFormState.fee),
});
await sendIt({
...transfer,
dry_run: false,
max_fee: parseInt(transferFormState.fee),
});
🤖 Prompt for AI Agents
In
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx
around lines 276 to 281, the call uses optional chaining on sendIt (await
sendIt?.({...})); remove the optional chaining and call sendIt directly. Ensure
sendIt is validated before this call (e.g., if (!sendIt) return or throw a clear
error) so the function is non-null when invoked, and keep the same payload
(dry_run: false, max_fee: parseInt(transferFormState.fee)).

Comment on lines +72 to +73
<Typography variant="body1">{formatCurrency(parseInt(transferFormState.fee) || 0)}</Typography>
</Box>

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.

🛠️ Refactor suggestion

Avoid parseInt without radix and potential precision loss on fees.

Fees come in minor units; prefer BigInt to keep exactness.

-          <Typography variant="body1">{formatCurrency(parseInt(transferFormState.fee) || 0)}</Typography>
+          <Typography variant="body1">
+            {(() => {
+              try {
+                const v = transferFormState.fee?.trim();
+                return formatCurrency(v ? BigInt(v) : 0n);
+              } catch {
+                return formatCurrency(0n);
+              }
+            })()}
+          </Typography>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Typography variant="body1">{formatCurrency(parseInt(transferFormState.fee) || 0)}</Typography>
</Box>
<Typography variant="body1">
{(() => {
try {
const v = transferFormState.fee?.trim();
return formatCurrency(v ? BigInt(v) : 0n);
} catch {
return formatCurrency(0n);
}
})()}
</Typography>
</Box>
🤖 Prompt for AI Agents
In
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/ConfirmationStep.tsx
around lines 72-73, the code uses parseInt(transferFormState.fee) without a
radix and as a Number which can lose precision for fee minor units; instead
parse the fee as a BigInt (e.g. BigInt(transferFormState.fee || '0')), perform
any minor->major unit conversion using BigInt arithmetic (divide by BigInt(10 **
decimals) or use a helper that accepts BigInt), then convert the resulting exact
value to the formatCurrency input (string or number as required) so formatting
preserves exactness and avoids parseInt precision/radix issues.

Comment on lines +84 to +89
<Box>
<Typography variant="subtitle2" color="text.secondary">
From:
</Typography>
<Typography variant="body1">{resource_address}</Typography>
</Box>

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.

🛠️ Refactor suggestion

Guard optional From address to avoid rendering “undefined”.

Also consider using CopyAddress for consistency.

-          <Typography variant="body1">{resource_address}</Typography>
+          <Typography variant="body1">
+            {resource_address ? <CopyAddress address={resource_address} /> : "-"}
+          </Typography>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Box>
<Typography variant="subtitle2" color="text.secondary">
From:
</Typography>
<Typography variant="body1">{resource_address}</Typography>
</Box>
<Box>
<Typography variant="subtitle2" color="text.secondary">
From:
</Typography>
<Typography variant="body1">
{resource_address ? <CopyAddress address={resource_address} /> : "-"}
</Typography>
</Box>
🤖 Prompt for AI Agents
In
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/ConfirmationStep.tsx
around lines 84 to 89, the code directly renders resource_address which can be
undefined; update the JSX to guard the optional value and avoid displaying
"undefined" and use the shared CopyAddress component for consistency:
conditionally render the CopyAddress component only when resource_address is
truthy (e.g., resource_address ? <CopyAddress address={resource_address} /> :
<Typography variant="body1" color="text.secondary">—</Typography>), or render a
short placeholder like "—" when absent; replace the current Typography that
shows resource_address with this conditional rendering and import CopyAddress if
not already imported.

Comment on lines +89 to +93
const enteredAmount = parseFloat(transferFormState.amount) || 0;
const hasInsufficientFunds = availableBalance !== undefined && enteredAmount > availableBalance;

const isFormValid = validateAddress(transferFormState.publicKey) && transferFormState.amount && !hasInsufficientFunds;

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.

🛠️ Refactor suggestion

Tighten validation: disallow zero/negative amounts and excess decimals.

Currently any non-empty string passes; extra decimals get silently floored later.

-  const enteredAmount = parseFloat(transferFormState.amount) || 0;
+  const enteredAmount = parseFloat(transferFormState.amount) || 0;
+  const decimals = transferFormState.amount.includes(".") ? (transferFormState.amount.split(".")[1]?.length || 0) : 0;
+  const exceedsDecimals = decimals > divisibility;
   const hasInsufficientFunds = availableBalance !== undefined && enteredAmount > availableBalance;

-  const isFormValid = validateAddress(transferFormState.publicKey) && transferFormState.amount && !hasInsufficientFunds;
+  const isFormValid =
+    validateAddress(transferFormState.publicKey) &&
+    enteredAmount > 0 &&
+    !hasInsufficientFunds &&
+    !exceedsDecimals;

Also surface the error in the amount field:

-          error={hasInsufficientFunds}
+          error={hasInsufficientFunds || exceedsDecimals}

Also applies to: 194-201

Comment on lines +206 to +216
helperText={
hasInsufficientFunds
? `Insufficient funds. Available balance: ${formatDisplayCurrency(availableBalance || 0)}`
: availableBalance !== undefined
? `Available balance: ${formatDisplayCurrency(availableBalance)}`
: undefined
}
InputProps={{
placeholder: "0.0",
endAdornment: token_symbol ? <InputAdornment position="end">{token_symbol}</InputAdornment> : undefined,
}}

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.

🛠️ Refactor suggestion

Available balance helper uses tXTR globally; show the asset’s symbol here.

formatDisplayCurrency hardcodes CURRENCY.SYMBOL. For non-XTR assets this misleads the user.

-          helperText={
-            hasInsufficientFunds
-              ? `Insufficient funds. Available balance: ${formatDisplayCurrency(availableBalance || 0)}`
-              : availableBalance !== undefined
-                ? `Available balance: ${formatDisplayCurrency(availableBalance)}`
-                : undefined
-          }
+          helperText={
+            hasInsufficientFunds
+              ? `Insufficient funds. Available balance: ${((availableBalance ?? 0).toLocaleString("en-US", { maximumFractionDigits: divisibility }))} ${token_symbol || CURRENCY.SYMBOL}`
+              : availableBalance !== undefined
+                ? `Available balance: ${availableBalance.toLocaleString("en-US", { maximumFractionDigits: divisibility })} ${token_symbol || CURRENCY.SYMBOL}`
+                : undefined
+          }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
helperText={
hasInsufficientFunds
? `Insufficient funds. Available balance: ${formatDisplayCurrency(availableBalance || 0)}`
: availableBalance !== undefined
? `Available balance: ${formatDisplayCurrency(availableBalance)}`
: undefined
}
InputProps={{
placeholder: "0.0",
endAdornment: token_symbol ? <InputAdornment position="end">{token_symbol}</InputAdornment> : undefined,
}}
helperText={
hasInsufficientFunds
? `Insufficient funds. Available balance: ${((availableBalance ?? 0).toLocaleString("en-US", { maximumFractionDigits: divisibility }))} ${token_symbol || CURRENCY.SYMBOL}`
: availableBalance !== undefined
? `Available balance: ${availableBalance.toLocaleString("en-US", { maximumFractionDigits: divisibility })} ${token_symbol || CURRENCY.SYMBOL}`
: undefined
}
InputProps={{
placeholder: "0.0",
endAdornment: token_symbol ? <InputAdornment position="end">{token_symbol}</InputAdornment> : undefined,
}}
🤖 Prompt for AI Agents
In
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/FormStep.tsx
around lines 206-216, the helperText uses formatDisplayCurrency which hardcodes
the XTR currency symbol and therefore shows tXTR for all assets; update the
helperText to display the current asset's symbol (token_symbol) instead by using
a version of formatDisplayCurrency that accepts/overrides the currency symbol or
by formatting the numeric value without a symbol and appending token_symbol
(falling back to CURRENCY.SYMBOL only if token_symbol is absent); ensure both
the "Insufficient funds" and "Available balance" messages use the asset symbol
and preserve the existing undefined behavior when availableBalance is not
provided.

Comment on lines +220 to +236
name="fee"
label="Fee"
value={
isEstimatingFee
? "Estimating..."
: transferFormState.fee
? (parseInt(transferFormState.fee) / CURRENCY.DIVISOR).toString()
: ""
}
placeholder={isEstimatingFee ? "Estimating..." : "Auto-calculated"}
onChange={onFormValueChange}
disabled={true}
style={{ flexGrow: 1 }}
InputProps={{
endAdornment: !isEstimatingFee && token_symbol ? <InputAdornment position="end">{token_symbol}</InputAdornment> : null,
}}
/>

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.

🛠️ Refactor suggestion

Fee shows wrong currency symbol; use network currency (tXTR), not the asset’s symbol.

Fee units are chain currency, but the endAdornment uses the resource token symbol. Also the field is disabled, so onChange is redundant.

Apply:

-          onChange={onFormValueChange}
           disabled={true}
           style={{ flexGrow: 1 }}
           InputProps={{
-            endAdornment: !isEstimatingFee && token_symbol ? <InputAdornment position="end">{token_symbol}</InputAdornment> : null,
+            endAdornment: !isEstimatingFee ? <InputAdornment position="end">{CURRENCY.SYMBOL}</InputAdornment> : null,
           }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
name="fee"
label="Fee"
value={
isEstimatingFee
? "Estimating..."
: transferFormState.fee
? (parseInt(transferFormState.fee) / CURRENCY.DIVISOR).toString()
: ""
}
placeholder={isEstimatingFee ? "Estimating..." : "Auto-calculated"}
onChange={onFormValueChange}
disabled={true}
style={{ flexGrow: 1 }}
InputProps={{
endAdornment: !isEstimatingFee && token_symbol ? <InputAdornment position="end">{token_symbol}</InputAdornment> : null,
}}
/>
name="fee"
label="Fee"
value={
isEstimatingFee
? "Estimating..."
: transferFormState.fee
? (parseInt(transferFormState.fee) / CURRENCY.DIVISOR).toString()
: ""
}
placeholder={isEstimatingFee ? "Estimating..." : "Auto-calculated"}
disabled={true}
style={{ flexGrow: 1 }}
InputProps={{
endAdornment: !isEstimatingFee ? <InputAdornment position="end">{CURRENCY.SYMBOL}</InputAdornment> : null,
}}
/>
🤖 Prompt for AI Agents
In
applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/FormStep.tsx
around lines 220-236, the fee field incorrectly shows the asset token symbol and
includes an unnecessary onChange while disabled; change the
InputProps.endAdornment to use the network currency symbol (tXTR) — e.g.,
networkCurrencySymbol or a constant representing the chain currency — instead of
token_symbol, remove the onChange prop (since the field is disabled), and ensure
placeholder/text still reflect estimating vs auto-calculated states.

Comment on lines +135 to +144
<SendMoneyDialog
open={resourceToSend !== null}
handleClose={() => setResourceToSend(null)}
onSendComplete={() => setResourceToSend(null)}
resource_address={resourceToSend?.address}
resource_type={resourceToSend?.resource_type!}
token_symbol={
balancesData?.balances.find((b) => b.resource_address === resourceToSend?.address)?.token_symbol || ""
}
/>

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.

⚠️ Potential issue

Avoid passing undefined required props; mount dialog only when a resource is selected.

Currently resource_type may be undefined while the dialog renders, risking runtime use of undefined.

-      <SendMoneyDialog
-        open={resourceToSend !== null}
-        handleClose={() => setResourceToSend(null)}
-        onSendComplete={() => setResourceToSend(null)}
-        resource_address={resourceToSend?.address}
-        resource_type={resourceToSend?.resource_type!}
-        token_symbol={
-          balancesData?.balances.find((b) => b.resource_address === resourceToSend?.address)?.token_symbol || ""
-        }
-      />
+      {resourceToSend && (
+        <SendMoneyDialog
+          open={true}
+          handleClose={() => setResourceToSend(null)}
+          onSendComplete={() => setResourceToSend(null)}
+          resource_address={resourceToSend.address}
+          resource_type={resourceToSend.resource_type}
+          token_symbol={
+            balancesData?.balances.find((b) => b.resource_address === resourceToSend.address)?.token_symbol || CURRENCY.SYMBOL
+          }
+        />
+      )}

Add import:

+import { CURRENCY } from "@utils/constants";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<SendMoneyDialog
open={resourceToSend !== null}
handleClose={() => setResourceToSend(null)}
onSendComplete={() => setResourceToSend(null)}
resource_address={resourceToSend?.address}
resource_type={resourceToSend?.resource_type!}
token_symbol={
balancesData?.balances.find((b) => b.resource_address === resourceToSend?.address)?.token_symbol || ""
}
/>
{resourceToSend && (
<SendMoneyDialog
open={true}
handleClose={() => setResourceToSend(null)}
onSendComplete={() => setResourceToSend(null)}
resource_address={resourceToSend.address}
resource_type={resourceToSend.resource_type}
token_symbol={
balancesData?.balances.find((b) => b.resource_address === resourceToSend.address)
?.token_symbol || CURRENCY.SYMBOL
}
/>
)}
🤖 Prompt for AI Agents
In applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx
around lines 135-144, the SendMoneyDialog is mounted even when resourceToSend
may be null which leads to passing undefined for required props (resource_type).
Fix by rendering/mounting the dialog only when resourceToSend is non-null (e.g.
resourceToSend && <SendMoneyDialog ... />), remove the non-null assertion on
resource_type and pass it directly from resourceToSend
(resource_type={resourceToSend.resource_type}), and compute token_symbol using
resourceToSend.address (no optional chaining on resourceToSend during prop
creation) so all required props are defined when the component mounts.

alias: {
"@": path.resolve(__dirname, "./src"),
"@components": path.resolve(__dirname, "./src/Components"),
"@components": path.resolve(__dirname, "./src/components"),

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.

💡 Verification agent

🧩 Analysis chain

Alias case fix to @components looks good; verify no leftover 'Components' paths.

Matches tsconfig. On case-sensitive filesystems, stray src/Components or imports to it will break builds.

Run to confirm:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Folders named 'components' vs 'Components' under web_ui/src:"
fd -td -HI 'components|Components' applications/tari_walletd/web_ui/src

echo "Legacy imports to '../../Components' (should be none):"
rg -nP --type=ts --type=tsx '(from|import\()\\s*[\"\\\'](?:\\./|\\.\\./)+Components/' applications/tari_walletd/web_ui/src || true

echo "Legacy alias '@Components' (should be none):"
rg -nP --type=ts --type=tsx 'from\\s+[\"\\\']@Components/' applications/tari_walletd/web_ui/src || true

Length of output: 644


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Searching for imports referencing uppercase 'Components' paths:"
rg -nP --type ts --type tsx "(?<=from\s+['\"])((\./|\.\./).*Components/)" applications/tari_walletd/web_ui/src || true

echo "Searching for any '@Components' alias usage:"
rg -n --type ts --type tsx "@Components" applications/tari_walletd/web_ui/src || true

Length of output: 221


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Checking for import statements referencing uppercase 'Components' directories:"
rg -nP "(?:from|import)\s+['\"].*?Components/" -g '*.ts' -g '*.tsx' applications/tari_walletd/web_ui/src || true

echo "Checking for any usage of '@Components' alias:"
rg -n "@Components" -g '*.ts' -g '*.tsx' applications/tari_walletd/web_ui/src || true

Length of output: 2033


Fix remaining uppercase ‘Components’ imports
Multiple files still import from ‘…/Components/…’ (e.g. routes/Accounts/Accounts.tsx, routes/WebauthnRegistration/Webauthn.tsx, routes/Wallet/Wallet.tsx, routes/Settings/Settings.tsx, etc.). Update all those to use the lowercase components directory or the @components alias to prevent build failures on case-sensitive filesystems.

🤖 Prompt for AI Agents
In applications/tari_walletd/web_ui/vite.config.ts around line 33, the alias
maps "@components" to the lowercase "./src/components", but many files still
import from "Components" (uppercase) which will break on case-sensitive
filesystems; update all imports across the project (e.g.,
routes/Accounts/Accounts.tsx, routes/WebauthnRegistration/Webauthn.tsx,
routes/Wallet/Wallet.tsx, routes/Settings/Settings.tsx, etc.) to use either the
lowercase path "./src/components/..." or the "@components/..." alias, by
performing a project-wide search for "/Components/" and replacing with
"/components/" or converting relative imports to use "@components" so all
imports match the directory name exactly.

@github-actions

Copy link
Copy Markdown

Test Results (CI)

419 tests  ±0   413 ✅ ±0   1h 21m 53s ⏱️ + 4m 15s
 69 suites ±0     0 💤 ±0 
  2 files   ±0     6 ❌ ±0 

For more details on these failures, see this check.

Results for commit 7120e7e. ± Comparison against base commit 8952029.

@sdbondi sdbondi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Look great thanks 👍

@sdbondi
sdbondi merged commit 2c938d1 into tari-project:development Sep 11, 2025
12 of 13 checks passed
@NovaT82
NovaT82 deleted the wallet-ui branch September 11, 2025 08:55
sdbondi added a commit to sdbondi/tari-ootle that referenced this pull request Sep 18, 2025
* development:
  feat(template_lib): adds engine schnorr signature verification (tari-project#1574)
  feat(wallet)!: add bech32 address with view-only key (tari-project#1573)
  feat(walletui): wallet ux improvements (tari-project#1572)
  fix(wallet)!: private derived tag and optimised* sync protocol (tari-project#1571)
  feat(walletui): send flow ux improvements (tari-project#1570)
  doc: update openrpc.json get_connections method (tari-project#1567)
  chore(deps): bump actions/setup-node from 4 to 5 (tari-project#1565)
@coderabbitai coderabbitai Bot mentioned this pull request Oct 6, 2025
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants