feat(walletui): send flow ux improvements - #1570
Conversation
WalkthroughIntroduces 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
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)
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks (2 passed, 1 warning)❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Poem
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.
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 unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
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 linkPrevents 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 FetchStatusCheckYou'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 objectUsing 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 numberPrefer 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 successAuto-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 stateAnnounce 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 feesPrevents 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 displayAvoids 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 keyAccount 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 renderValuemap(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
📒 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 fortXTMandformatXTMas well as all uses ofCURRENCY.SYMBOLinapplications/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 goodSendMoney 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 LGTMSwitch 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 LGTMLoading import via @components is correct.
applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Components/Registration.tsx (1)
18-22: Signature reformat LGTMNo 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?
| if (!account) { | ||
| return null; | ||
| } | ||
|
|
||
| const { data } = useAccountsGetBalances(substateIdToString(account.address)); |
There was a problem hiding this comment.
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.
| 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) { |
There was a problem hiding this comment.
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.
| 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.
| await sendIt?.({ | ||
| ...transfer, | ||
| dry_run: false, | ||
| max_fee: parseInt(transferFormState.fee), | ||
| }); | ||
|
|
There was a problem hiding this comment.
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.
| 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)).
| <Typography variant="body1">{formatCurrency(parseInt(transferFormState.fee) || 0)}</Typography> | ||
| </Box> |
There was a problem hiding this comment.
🛠️ 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.
| <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.
| <Box> | ||
| <Typography variant="subtitle2" color="text.secondary"> | ||
| From: | ||
| </Typography> | ||
| <Typography variant="body1">{resource_address}</Typography> | ||
| </Box> |
There was a problem hiding this comment.
🛠️ 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.
| <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.
| const enteredAmount = parseFloat(transferFormState.amount) || 0; | ||
| const hasInsufficientFunds = availableBalance !== undefined && enteredAmount > availableBalance; | ||
|
|
||
| const isFormValid = validateAddress(transferFormState.publicKey) && transferFormState.amount && !hasInsufficientFunds; | ||
|
|
There was a problem hiding this comment.
🛠️ 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
| 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, | ||
| }} |
There was a problem hiding this comment.
🛠️ 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.
| 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.
| 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, | ||
| }} | ||
| /> |
There was a problem hiding this comment.
🛠️ 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.
| 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.
| <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 || "" | ||
| } | ||
| /> |
There was a problem hiding this comment.
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.
| <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"), |
There was a problem hiding this comment.
💡 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 || trueLength 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 || trueLength 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 || trueLength 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.
Test Results (CI)419 tests ±0 413 ✅ ±0 1h 21m 53s ⏱️ + 4m 15s For more details on these failures, see this check. Results for commit 7120e7e. ± Comparison against base commit 8952029. |
* 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)
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.
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
Summary by CodeRabbit
New Features
Refactor
Chores