From 36619e2aac373b30f65375a782367cf1f9ca084f Mon Sep 17 00:00:00 2001 From: neocybereth Date: Fri, 21 Nov 2025 23:56:09 +1300 Subject: [PATCH 01/17] fix: add addresses to shielded transfer button --- .../src/App/AccountOverview/ShieldedAssetsOverview.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/namadillo/src/App/AccountOverview/ShieldedAssetsOverview.tsx b/apps/namadillo/src/App/AccountOverview/ShieldedAssetsOverview.tsx index dab4ee1b6b..dfafae1463 100644 --- a/apps/namadillo/src/App/AccountOverview/ShieldedAssetsOverview.tsx +++ b/apps/namadillo/src/App/AccountOverview/ShieldedAssetsOverview.tsx @@ -1,7 +1,8 @@ import { ActionButton, Panel } from "@namada/components"; import { MaspSyncCover } from "App/Common/MaspSyncCover"; import { ShieldedAssetTable } from "App/Masp/ShieldedAssetTable"; -import { routes } from "App/routes"; +import { params, routes } from "App/routes"; +import { defaultShieldedAccountAtom } from "atoms/accounts"; import { applicationFeaturesAtom } from "atoms/settings"; import clsx from "clsx"; import { useAmountsInFiat } from "hooks/useAmountsInFiat"; @@ -16,6 +17,7 @@ export const ShieldedAssetsOverview = (): JSX.Element => { useAmountsInFiat(); const textContainerClassList = `flex h-full gap-1 items-center justify-center`; const requiresNewShieldedSync = useRequiresNewShieldedSync(); + const shieldedAccount = useAtomValue(defaultShieldedAccountAtom); // Hide TotalBalanceCard if shielded fiat amount is 0 but shielded assets exist const shouldHideBalanceCard = @@ -31,7 +33,7 @@ export const ShieldedAssetsOverview = (): JSX.Element => { footerButtons={ <> Date: Sat, 22 Nov 2025 00:04:19 +1300 Subject: [PATCH 02/17] fix: stakign table descending sorting shows opposite arrow --- apps/namadillo/src/utils/sorting.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/namadillo/src/utils/sorting.ts b/apps/namadillo/src/utils/sorting.ts index 4dd0c5c39a..192964ade3 100644 --- a/apps/namadillo/src/utils/sorting.ts +++ b/apps/namadillo/src/utils/sorting.ts @@ -7,9 +7,9 @@ export const compareBigNumbers = ( isDescending: boolean ): number => { if (n1 === undefined && n2 === undefined) return 0; - if (!n1) return isDescending ? 1 : -1; - if (!n2) return isDescending ? -1 : 1; - return isDescending ? n2.minus(n1).toNumber() : n1.minus(n2).toNumber(); + if (!n1) return isDescending ? -1 : 1; + if (!n2) return isDescending ? 1 : -1; + return isDescending ? n1.minus(n2).toNumber() : n2.minus(n1).toNumber(); }; export const compareStrings = ( @@ -17,7 +17,7 @@ export const compareStrings = ( str2: string, isDescending: boolean ): number => { - return isDescending ? str2.localeCompare(str1) : str1.localeCompare(str2); + return isDescending ? str1.localeCompare(str2) : str2.localeCompare(str1); }; const compareNumbers = ( @@ -25,8 +25,8 @@ const compareNumbers = ( b: number, isDescending: boolean ): number => { - if (a > b) return isDescending ? -1 : 1; - if (a < b) return isDescending ? 1 : -1; + if (a > b) return isDescending ? 1 : -1; + if (a < b) return isDescending ? -1 : 1; return 0; }; From cb5a4f1144473a720b72029f8e660854d17ab1fe Mon Sep 17 00:00:00 2001 From: neocybereth Date: Sat, 22 Nov 2025 00:18:22 +1300 Subject: [PATCH 03/17] fix: get filtering workig in selecttoken --- .../src/App/Transfer/SelectToken.tsx | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/apps/namadillo/src/App/Transfer/SelectToken.tsx b/apps/namadillo/src/App/Transfer/SelectToken.tsx index 433462fe68..139f8f3a00 100644 --- a/apps/namadillo/src/App/Transfer/SelectToken.tsx +++ b/apps/namadillo/src/App/Transfer/SelectToken.tsx @@ -92,25 +92,31 @@ export const SelectToken = ({ const filteredTokens = useMemo(() => { return assetsWithAmounts .filter((assetWithAmount) => { - if (assetWithAmount.amount.eq(0)) return false; - - // Filter by search term + // Filter by search term (if provided) const matchesSearch = - !!filter && + !filter || assetWithAmount.asset.name .toLowerCase() .includes(filter.toLowerCase()); + // Filter by network (if provided) const matchesNetwork = + !selectedNetwork || selectedNetwork === - assetWithAmount.asset.traces?.[0]?.counterparty?.chain_name; - return !selectedNetwork ? true : matchesSearch || matchesNetwork; + assetWithAmount.asset.traces?.[0]?.counterparty?.chain_name; + + // Filter out zero balances + const hasBalance = !assetWithAmount.amount.eq(0); + + // All conditions must be true + return matchesSearch && matchesNetwork && hasBalance; }) .sort((a, b) => Number(b.amount) - Number(a.amount)); }, [assetsWithAmounts, filter, selectedNetwork, assetToNetworkMap]); const handleNetworkSelect = (networkName: string): void => { setSelectedNetwork(selectedNetwork === networkName ? null : networkName); + setFilter(""); // Clear search input when network is selected }; const handleWalletAddressChange = (address: string): void => { @@ -224,7 +230,10 @@ export const SelectToken = ({ >
  • ))} diff --git a/apps/namadillo/src/hooks/useTransactionCallbacks.tsx b/apps/namadillo/src/hooks/useTransactionCallbacks.tsx index 671f68b0e5..02dc34f651 100644 --- a/apps/namadillo/src/hooks/useTransactionCallbacks.tsx +++ b/apps/namadillo/src/hooks/useTransactionCallbacks.tsx @@ -1,8 +1,15 @@ +import { + isIbcAddress, + isShieldedAddress, + isTransparentAddress, +} from "App/Transfer/common"; import { accountBalanceAtom, defaultAccountAtom } from "atoms/accounts"; import { shieldedBalanceAtom } from "atoms/balance/atoms"; import { shouldUpdateBalanceAtom, shouldUpdateProposalAtom } from "atoms/etc"; import { claimableRewardsAtom } from "atoms/staking"; -import { useAtomValue, useSetAtom } from "jotai"; +import { recentAddressesAtom } from "atoms/transactions"; +import { getAddressLabel } from "atoms/transfer/functions"; +import { useAtom, useAtomValue, useSetAtom } from "jotai"; import { TransferStep, TransferTransactionData } from "types"; import { useTransactionEventListener } from "utils"; import { useTransactionActions } from "./useTransactionActions"; @@ -16,6 +23,42 @@ export const useTransactionCallback = (): void => { const { changeTransaction } = useTransactionActions(); const shouldUpdateProposal = useSetAtom(shouldUpdateProposalAtom); const shouldUpdateBalance = useSetAtom(shouldUpdateBalanceAtom); + const [recentAddresses, setRecentAddresses] = useAtom(recentAddressesAtom); + + const updateRecentAddressTimestamp = (destinationAddress: string): void => { + // Determine the address type + let addressType: "transparent" | "shielded" | "ibc"; + if (isTransparentAddress(destinationAddress)) { + addressType = "transparent"; + } else if (isShieldedAddress(destinationAddress)) { + addressType = "shielded"; + } else if (isIbcAddress(destinationAddress)) { + addressType = "ibc"; + } else { + return; // Unknown address type, don't update + } + + // Find existing entry to preserve its label + const existing = recentAddresses.find( + (recent) => recent.address === destinationAddress + ); + + // Add updated entry at the beginning, remove old entry, and keep only last 10 + setRecentAddresses( + [ + { + address: destinationAddress, + type: addressType, + label: + existing?.label || getAddressLabel(destinationAddress, addressType), + timestamp: Date.now(), + }, + ...recentAddresses.filter( + (recent) => recent.address !== destinationAddress + ), + ].slice(0, 10) + ); + }; const onBalanceUpdate = (): void => { // TODO: refactor this after event subscription is enabled on indexer @@ -56,6 +99,11 @@ export const useTransactionCallback = (): void => { refetchBalances(); refetchShieldedBalance(); + // Update recent addresses timestamp for the destination + if (e.detail.destinationAddress) { + updateRecentAddressTimestamp(e.detail.destinationAddress); + } + const timePolling = 6 * 1000; setTimeout(() => shouldUpdateBalance(false), timePolling); }; diff --git a/apps/namadillo/src/utils/dates.ts b/apps/namadillo/src/utils/dates.ts index ef06b1df5f..365476774f 100644 --- a/apps/namadillo/src/utils/dates.ts +++ b/apps/namadillo/src/utils/dates.ts @@ -25,3 +25,9 @@ export const secondsToTimeRemainingString = ( secondsToDateTime(endTimeInSeconds) .diff(secondsToDateTime(startTimeInSeconds), ["days", "hours", "minutes"]) .toHuman({ maximumFractionDigits: 0 }); + +export const timestampToRelativeTime = (timestamp: number): string => { + const dateTime = DateTime.fromMillis(timestamp); + const relative = dateTime.toRelative(); + return relative || "1m ago"; +}; From 27f153c03fb7c02757431957339e3dbf5412795e Mon Sep 17 00:00:00 2001 From: neocybereth Date: Tue, 25 Nov 2025 14:39:58 +1300 Subject: [PATCH 08/17] fix: make CTA button on transfer module match flow --- .../src/App/Transfer/TransferModule.tsx | 12 ++++++++++- apps/namadillo/src/App/Transfer/utils.ts | 20 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/apps/namadillo/src/App/Transfer/TransferModule.tsx b/apps/namadillo/src/App/Transfer/TransferModule.tsx index 0de567beb0..a0a913d870 100644 --- a/apps/namadillo/src/App/Transfer/TransferModule.tsx +++ b/apps/namadillo/src/App/Transfer/TransferModule.tsx @@ -36,7 +36,11 @@ import { TransferArrow } from "./TransferArrow"; import { TransferDestination } from "./TransferDestination"; import { TransferSource } from "./TransferSource"; import { TransferModuleProps, ValidationResult } from "./types"; -import { getButtonText, validateTransferForm } from "./utils"; +import { + determineTransferType, + getButtonText, + validateTransferForm, +} from "./utils"; export const TransferModule = ({ source, @@ -108,6 +112,12 @@ export const TransferModule = ({ validationResult, availableAmountMinusFees, buttonTextErrors, + transactionType: determineTransferType({ + sourceAddress, + destinationAddress, + }), + sourceAddress, + destinationAddress, }); }; diff --git a/apps/namadillo/src/App/Transfer/utils.ts b/apps/namadillo/src/App/Transfer/utils.ts index 465324aa0b..f79c1f9996 100644 --- a/apps/namadillo/src/App/Transfer/utils.ts +++ b/apps/namadillo/src/App/Transfer/utils.ts @@ -159,13 +159,21 @@ export const getButtonText = ({ validationResult, availableAmountMinusFees, buttonTextErrors = {}, + transactionType, + sourceAddress, + destinationAddress, }: { isSubmitting: boolean | undefined; submittingText?: string; validationResult: ValidationResult; availableAmountMinusFees: BigNumber | undefined; buttonTextErrors?: Partial>; + transactionType: TransferType; + sourceAddress: string; + destinationAddress: string; }): string => { + const sourceIsShielded = isShieldedAddress(sourceAddress ?? ""); + const destinationIsShielded = isShieldedAddress(destinationAddress ?? ""); if (isSubmitting) { return "Submitting..."; } @@ -202,6 +210,18 @@ export const getButtonText = ({ return getText("Wallet amount not available"); } + // Update CTA to match transaction type + if (transactionType === "shield" || destinationIsShielded) + return getText("Shield"); + if ( + transactionType === "unshield" || + (sourceIsShielded && !destinationIsShielded) + ) + return getText("Unshield"); + if (transactionType === "ibc-deposit") return getText("IBC Deposit"); + if (transactionType === "ibc-withdraw") return getText("IBC Withdraw"); + if (transactionType === "namada-transfer") return getText("Transfer"); + return "Submit"; }; From d019239b5b3eb3e04e632a7f120c7cbcce997759 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Tue, 25 Nov 2025 14:51:37 +1300 Subject: [PATCH 09/17] fix: destination address disabled when there was no source address --- apps/namadillo/src/App/Transfer/TransferDestination.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/namadillo/src/App/Transfer/TransferDestination.tsx b/apps/namadillo/src/App/Transfer/TransferDestination.tsx index 1e4ab46673..9ee9f363d9 100644 --- a/apps/namadillo/src/App/Transfer/TransferDestination.tsx +++ b/apps/namadillo/src/App/Transfer/TransferDestination.tsx @@ -211,9 +211,7 @@ export const TransferDestination = ({ :