From de2fe5934193991d1cb35a8d8e790dc523786bbe Mon Sep 17 00:00:00 2001 From: neocybereth Date: Thu, 20 Nov 2025 08:44:20 +1300 Subject: [PATCH 01/10] fix: filter out IBC assets for destination if IBC is the source --- .../App/Transfer/DestinationAddressModal.tsx | 18 +++++++++++++++--- apps/namadillo/src/constants.ts | 2 ++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/apps/namadillo/src/App/Transfer/DestinationAddressModal.tsx b/apps/namadillo/src/App/Transfer/DestinationAddressModal.tsx index 8305573a66..89f5a9e288 100644 --- a/apps/namadillo/src/App/Transfer/DestinationAddressModal.tsx +++ b/apps/namadillo/src/App/Transfer/DestinationAddressModal.tsx @@ -15,7 +15,11 @@ import { useCallback, useState } from "react"; import { Address, Asset } from "types"; import namadaShieldedIcon from "./assets/namada-shielded.svg"; import namadaTransparentIcon from "./assets/namada-transparent.svg"; -import { isShieldedAddress, isTransparentAddress } from "./common"; +import { + isIbcAddress, + isShieldedAddress, + isTransparentAddress, +} from "./common"; export type ValidationError = { type: "invalid-format" | "unsupported-chain" | "empty"; @@ -67,6 +71,11 @@ export const DestinationAddressModal = ({ const isSourceAddressMatch = (address: string): boolean => address === sourceAddress; + const isSourceIbc = isIbcAddress(sourceAddress); + const filterNonIbcIfSourceIbc = ( + items: T[] + ): T[] => (isSourceIbc ? items.filter((item) => item.type !== "ibc") : items); + // Build your addresses options const addressOptions: AddressOption[] = []; if (accounts) { @@ -96,7 +105,7 @@ export const DestinationAddressModal = ({ }); } } - if (keplrAddress) + if (keplrAddress && !isSourceIbc) { addressOptions.push({ id: "keplr", label: "Keplr Address", @@ -107,10 +116,13 @@ export const DestinationAddressModal = ({ : getChainImageUrl(getChainFromAddress(keplrAddress ?? "")), type: "keplr", }); + } + const addressOptionsAddresses = addressOptions.map((addr) => addr.address); // Build recent addresses options - const recentAddressOptions: AddressOption[] = recentAddresses + const filteredRecentAddresses = filterNonIbcIfSourceIbc(recentAddresses); + const recentAddressOptions: AddressOption[] = filteredRecentAddresses .filter((addresses) => !addressOptionsAddresses.includes(addresses.address)) .map((recent) => ({ id: `recent-${recent.address}`, diff --git a/apps/namadillo/src/constants.ts b/apps/namadillo/src/constants.ts index e69de29bb2..139597f9cb 100644 --- a/apps/namadillo/src/constants.ts +++ b/apps/namadillo/src/constants.ts @@ -0,0 +1,2 @@ + + From 31f5e2266dd181bdde9b29f4b398f2cb6802e44f Mon Sep 17 00:00:00 2001 From: neocybereth Date: Thu, 20 Nov 2025 13:46:32 +1300 Subject: [PATCH 02/10] fix: add more caching to all keplr assets call --- apps/namadillo/src/atoms/integrations/atoms.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/namadillo/src/atoms/integrations/atoms.ts b/apps/namadillo/src/atoms/integrations/atoms.ts index f6f60c51f5..f41376a29a 100644 --- a/apps/namadillo/src/atoms/integrations/atoms.ts +++ b/apps/namadillo/src/atoms/integrations/atoms.ts @@ -150,20 +150,19 @@ export const allKeplrAssetsBalanceAtom = atomWithQuery< chainAssetsMap.data, connectedWallets, ], + staleTime: 30_000, // Consider data fresh for 30 seconds + gcTime: 5 * 60_000, // Keep in cache for 5 minutes after last use + refetchOnMount: false, // Don't refetch if data is fresh + refetchOnWindowFocus: false, // Don't refetch on window focus ...queryDependentFn(async () => { invariant(chainSettings.data, "No chain settings"); invariant(chainAssetsMap.data, "No chain assets map"); // Only proceed if Keplr is connected - if (!connectedWallets?.keplr) { - return {}; - } - + if (!connectedWallets?.keplr) return {}; const availableChains = getAvailableChains(); - // Get Keplr wallet instance const keplr = getKeplrWallet(); - // First, silently check which chains we're already connected to const connectedChains: { chain: Chain; walletAddress: string }[] = []; From 3f1ef11ca8e7f9f981f7f9bdaa342034ee8eaa77 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Thu, 20 Nov 2025 14:12:42 +1300 Subject: [PATCH 03/10] fix: cleanup chains to be only getAvailableChains to avoid stride mixing with stratos --- apps/namadillo/src/App/Transfer/AddressDropdown.tsx | 2 -- apps/namadillo/src/App/Transfer/SelectToken.tsx | 7 +------ apps/namadillo/src/App/Transfer/TransferModule.tsx | 1 - apps/namadillo/src/integrations/utils.ts | 3 ++- 4 files changed, 3 insertions(+), 10 deletions(-) diff --git a/apps/namadillo/src/App/Transfer/AddressDropdown.tsx b/apps/namadillo/src/App/Transfer/AddressDropdown.tsx index c2ba3eacb0..4088de1f40 100644 --- a/apps/namadillo/src/App/Transfer/AddressDropdown.tsx +++ b/apps/namadillo/src/App/Transfer/AddressDropdown.tsx @@ -70,7 +70,6 @@ export const AddressDropdown = ({ const chain = getChainFromAddress(selectedAddress); chainId = chain?.chain_id; } - // Fallback to first available chain if we couldn't determine from selectedAddress if (!chainId) { const availableChains = getAvailableChains(); @@ -144,7 +143,6 @@ export const AddressDropdown = ({ }); } } - // Add Keplr option only if we have a connected address if (keplrAddress) { addressOptions.push({ diff --git a/apps/namadillo/src/App/Transfer/SelectToken.tsx b/apps/namadillo/src/App/Transfer/SelectToken.tsx index 4921bb71cd..433462fe68 100644 --- a/apps/namadillo/src/App/Transfer/SelectToken.tsx +++ b/apps/namadillo/src/App/Transfer/SelectToken.tsx @@ -136,18 +136,13 @@ export const SelectToken = ({ try { const keplrInstance = await keplrWallet.get(); // Keplr is not installed, redirect to download page - if (!keplrInstance) { - keplrWallet.install(); - return; - } - + if (!keplrInstance) return keplrWallet.install(); const targetChainRegistry = getChainRegistryByChainName( token.chainName ); invariant(targetChainRegistry, "Target chain registry not found"); const chainId = targetChainRegistry.chain.chain_id; await connectToChainId(chainId); - // Update connected wallets state only after successful connection setConnectedWallets((obj: Record) => ({ ...obj, diff --git a/apps/namadillo/src/App/Transfer/TransferModule.tsx b/apps/namadillo/src/App/Transfer/TransferModule.tsx index 05ddaf2236..4ec8077912 100644 --- a/apps/namadillo/src/App/Transfer/TransferModule.tsx +++ b/apps/namadillo/src/App/Transfer/TransferModule.tsx @@ -324,7 +324,6 @@ export const TransferModule = ({ newParams.set(params.asset, selectedAssetWithAmount.asset.symbol); return newParams; }); - source.onChangeAmount(undefined); source.onChangeSelectedAsset(selectedAssetWithAmount); setAssetSelectorModalOpen(false); diff --git a/apps/namadillo/src/integrations/utils.ts b/apps/namadillo/src/integrations/utils.ts index a7037d5381..2c3fc18e80 100644 --- a/apps/namadillo/src/integrations/utils.ts +++ b/apps/namadillo/src/integrations/utils.ts @@ -6,12 +6,12 @@ import tokenImage from "App/Common/assets/token.svg"; import namadaTransparentSvg from "App/Transfer/assets/namada-transparent.svg"; import { isShieldedAddress, isTransparentAddress } from "App/Transfer/common"; import { + getAvailableChains, getChainRegistryByChainName, getRestApiAddressByIndex, getRpcByIndex, } from "atoms/integrations"; import BigNumber from "bignumber.js"; -import { chains } from "chain-registry"; import { Asset, @@ -44,6 +44,7 @@ export const findRegistryByChainId = ( }; export const getChainFromAddress = (address: string): Chain | undefined => { + const chains = getAvailableChains(); if (isShieldedAddress(address) || isTransparentAddress(address)) { return chains.find((chain) => chain.chain_name === "namada") as Chain; } else { From bb4374a8f52a1ad62ebae7ae09a900e5260c3796 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Thu, 20 Nov 2025 14:18:14 +1300 Subject: [PATCH 04/10] fix: remove extra file --- apps/namadillo/src/App/Transfer/AddressDropdown.tsx | 2 ++ apps/namadillo/src/App/Transfer/DestinationAddressModal.tsx | 6 ++---- apps/namadillo/src/constants.ts | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) delete mode 100644 apps/namadillo/src/constants.ts diff --git a/apps/namadillo/src/App/Transfer/AddressDropdown.tsx b/apps/namadillo/src/App/Transfer/AddressDropdown.tsx index 4088de1f40..c2ba3eacb0 100644 --- a/apps/namadillo/src/App/Transfer/AddressDropdown.tsx +++ b/apps/namadillo/src/App/Transfer/AddressDropdown.tsx @@ -70,6 +70,7 @@ export const AddressDropdown = ({ const chain = getChainFromAddress(selectedAddress); chainId = chain?.chain_id; } + // Fallback to first available chain if we couldn't determine from selectedAddress if (!chainId) { const availableChains = getAvailableChains(); @@ -143,6 +144,7 @@ export const AddressDropdown = ({ }); } } + // Add Keplr option only if we have a connected address if (keplrAddress) { addressOptions.push({ diff --git a/apps/namadillo/src/App/Transfer/DestinationAddressModal.tsx b/apps/namadillo/src/App/Transfer/DestinationAddressModal.tsx index 89f5a9e288..cead2e9dba 100644 --- a/apps/namadillo/src/App/Transfer/DestinationAddressModal.tsx +++ b/apps/namadillo/src/App/Transfer/DestinationAddressModal.tsx @@ -70,11 +70,9 @@ export const DestinationAddressModal = ({ // Dont display an address if it matches the source address const isSourceAddressMatch = (address: string): boolean => address === sourceAddress; - const isSourceIbc = isIbcAddress(sourceAddress); - const filterNonIbcIfSourceIbc = ( - items: T[] - ): T[] => (isSourceIbc ? items.filter((item) => item.type !== "ibc") : items); + const filterNonIbcIfSourceIbc = (items: RecentAddress[]): RecentAddress[] => + isSourceIbc ? items.filter((item) => item.type !== "ibc") : items; // Build your addresses options const addressOptions: AddressOption[] = []; diff --git a/apps/namadillo/src/constants.ts b/apps/namadillo/src/constants.ts deleted file mode 100644 index 139597f9cb..0000000000 --- a/apps/namadillo/src/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ - - From 5cfaf5f5489e91555906ca59767d108119e2e798 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Thu, 20 Nov 2025 14:19:50 +1300 Subject: [PATCH 05/10] fix: cleanup --- apps/namadillo/src/App/Transfer/TransferModule.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/namadillo/src/App/Transfer/TransferModule.tsx b/apps/namadillo/src/App/Transfer/TransferModule.tsx index 4ec8077912..05ddaf2236 100644 --- a/apps/namadillo/src/App/Transfer/TransferModule.tsx +++ b/apps/namadillo/src/App/Transfer/TransferModule.tsx @@ -324,6 +324,7 @@ export const TransferModule = ({ newParams.set(params.asset, selectedAssetWithAmount.asset.symbol); return newParams; }); + source.onChangeAmount(undefined); source.onChangeSelectedAsset(selectedAssetWithAmount); setAssetSelectorModalOpen(false); From 3128e4980604c4a3cce462b1c4f81aa77d1bddb2 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Thu, 20 Nov 2025 14:24:54 +1300 Subject: [PATCH 06/10] fix: fix size of image in destination dropdown --- apps/namadillo/src/App/Transfer/TransferDestination.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/namadillo/src/App/Transfer/TransferDestination.tsx b/apps/namadillo/src/App/Transfer/TransferDestination.tsx index baa17cab40..8d6f9fb494 100644 --- a/apps/namadillo/src/App/Transfer/TransferDestination.tsx +++ b/apps/namadillo/src/App/Transfer/TransferDestination.tsx @@ -228,7 +228,7 @@ export const TransferDestination = ({ getChainFromAddress(destinationAddress ?? "") ?.pretty_name } - className="w-7" + className="w-10" />
From 7508093cd46e292ceb9f243ef5393e3129bc1a16 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Thu, 20 Nov 2025 14:49:24 +1300 Subject: [PATCH 07/10] fix: tests --- .../src/App/Swap/__tests__/SelectAssetModal.test.tsx | 6 ++++++ .../src/App/Transfer/__tests__/SelectChainModal.test.tsx | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/apps/namadillo/src/App/Swap/__tests__/SelectAssetModal.test.tsx b/apps/namadillo/src/App/Swap/__tests__/SelectAssetModal.test.tsx index 606ec39174..2c8b801271 100644 --- a/apps/namadillo/src/App/Swap/__tests__/SelectAssetModal.test.tsx +++ b/apps/namadillo/src/App/Swap/__tests__/SelectAssetModal.test.tsx @@ -9,6 +9,12 @@ jest.mock("hooks/useIsChannelInactive", () => ({ })), })); +// Mock the getAvailableChains function +jest.mock("atoms/integrations", () => ({ + ...jest.requireActual("atoms/integrations"), + getAvailableChains: jest.fn(() => []), +})); + describe("SwapSelectAssetModal", () => { const onCloseMock = jest.fn(); const onSelectMock = jest.fn(); diff --git a/apps/namadillo/src/App/Transfer/__tests__/SelectChainModal.test.tsx b/apps/namadillo/src/App/Transfer/__tests__/SelectChainModal.test.tsx index 23f3a0c3ff..e44798a07f 100644 --- a/apps/namadillo/src/App/Transfer/__tests__/SelectChainModal.test.tsx +++ b/apps/namadillo/src/App/Transfer/__tests__/SelectChainModal.test.tsx @@ -4,6 +4,12 @@ import { namadaChainMock, randomChainMock } from "App/Common/__mocks__/chains"; import { walletMock } from "App/Common/__mocks__/providers"; import { SelectChainModal } from "App/Transfer/SelectChainModal"; +// Mock the getAvailableChains function +jest.mock("atoms/integrations", () => ({ + ...jest.requireActual("atoms/integrations"), + getAvailableChains: jest.fn(() => []), +})); + describe("Component: SelectChainModal", () => { const mockChains = [randomChainMock, namadaChainMock]; const mockAddress = "cosmos1xnu3p06fkke8hnl7t83hzhggrca59syf0wjqgh"; From c32f6f3d00e56f3934f28148aa2761b8b9dee7f1 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Thu, 20 Nov 2025 23:46:37 +1300 Subject: [PATCH 08/10] fix: fix not beign able to select neutron or noble because "n" came first --- apps/namadillo/src/integrations/utils.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/namadillo/src/integrations/utils.ts b/apps/namadillo/src/integrations/utils.ts index 2c3fc18e80..d60fc1c330 100644 --- a/apps/namadillo/src/integrations/utils.ts +++ b/apps/namadillo/src/integrations/utils.ts @@ -49,8 +49,17 @@ export const getChainFromAddress = (address: string): Chain | undefined => { return chains.find((chain) => chain.chain_name === "namada") as Chain; } else { // Connect to IBC chain and then return the registered chain - const chain = chains.find( - (chain) => chain.bech32_prefix && address.startsWith(chain.bech32_prefix) + // Sort chains by bech32_prefix length (longest first) to avoid prefix collision + // e.g., "noble" should be checked before "n" (NYM) to prevent mismatches + const sortedChains = chains + .filter((chain) => chain.bech32_prefix) + .sort( + (a, b) => + (b.bech32_prefix?.length || 0) - (a.bech32_prefix?.length || 0) + ); + + const chain = sortedChains.find((chain) => + address.startsWith(chain.bech32_prefix + "1") ); return chain as Chain | undefined; } From 963752da6310151b11084396e387ae28263fddf1 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Thu, 20 Nov 2025 23:56:46 +1300 Subject: [PATCH 09/10] fix: make sure recent addresses doesn't have copies of currently selected address --- apps/namadillo/src/App/Transfer/DestinationAddressModal.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/namadillo/src/App/Transfer/DestinationAddressModal.tsx b/apps/namadillo/src/App/Transfer/DestinationAddressModal.tsx index cead2e9dba..39c0ca70f4 100644 --- a/apps/namadillo/src/App/Transfer/DestinationAddressModal.tsx +++ b/apps/namadillo/src/App/Transfer/DestinationAddressModal.tsx @@ -122,6 +122,7 @@ export const DestinationAddressModal = ({ const filteredRecentAddresses = filterNonIbcIfSourceIbc(recentAddresses); const recentAddressOptions: AddressOption[] = filteredRecentAddresses .filter((addresses) => !addressOptionsAddresses.includes(addresses.address)) + .filter((addresses) => addresses.address !== sourceAddress) .map((recent) => ({ id: `recent-${recent.address}`, label: recent.label || getAddressLabel(recent.address, recent.type), From 1016092f436eb65b246e379f19012b4c8c5307d3 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Fri, 21 Nov 2025 00:24:19 +1300 Subject: [PATCH 10/10] fix: memos for shielding and unshielding --- apps/namadillo/src/App/Masp/MaspShield.tsx | 6 +++++- apps/namadillo/src/App/Masp/MaspUnshield.tsx | 3 +++ .../src/App/Transfer/TransferDestination.tsx | 13 +++++++++++-- apps/namadillo/src/App/Transfer/TransferModule.tsx | 2 ++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/apps/namadillo/src/App/Masp/MaspShield.tsx b/apps/namadillo/src/App/Masp/MaspShield.tsx index 6da776260d..305ad9ce18 100644 --- a/apps/namadillo/src/App/Masp/MaspShield.tsx +++ b/apps/namadillo/src/App/Masp/MaspShield.tsx @@ -15,7 +15,7 @@ import invariant from "invariant"; import { useAtom, useAtomValue } from "jotai"; import { createTransferDataFromNamada } from "lib/transactions"; import { useEffect, useState } from "react"; -import { Link } from "react-router-dom"; +import { Link, useLocation } from "react-router-dom"; import { AssetWithAmountAndChain } from "types"; interface MaspShieldProps { sourceAddress: string; @@ -35,6 +35,7 @@ export const MaspShield = ({ setAssetSelectorModalOpen, }: MaspShieldProps): JSX.Element => { // COMPONENT STATE + const [memo, setMemo] = useState(""); const [displayAmount, setDisplayAmount] = useAtom(transferAmountAtom); const [selectedAssetWithAmount, setSelectedAssetWithAmount] = useState< AssetWithAmountAndChain | undefined @@ -49,6 +50,7 @@ export const MaspShield = ({ const chainParameters = useAtomValue(chainParametersAtom); const defaultAccounts = useAtomValue(allDefaultAccountsAtom); const [ledgerStatus, setLedgerStatusStop] = useAtom(ledgerStatusDataAtom); + const { pathname } = useLocation(); // DERIVED VALUES const transparentAddress = defaultAccounts.data?.find( (account) => account.type !== AccountType.ShieldedKeys @@ -167,6 +169,8 @@ export const MaspShield = ({ address: destinationAddress, isShieldedAddress: true, onChangeAddress: setDestinationAddress, + memo, + onChangeMemo: pathname !== routes.shield ? setMemo : undefined, }} feeProps={feeProps} isSubmitting={isPerformingTransfer || isSuccess} diff --git a/apps/namadillo/src/App/Masp/MaspUnshield.tsx b/apps/namadillo/src/App/Masp/MaspUnshield.tsx index ddcd4e8d5e..557036050b 100644 --- a/apps/namadillo/src/App/Masp/MaspUnshield.tsx +++ b/apps/namadillo/src/App/Masp/MaspUnshield.tsx @@ -34,6 +34,7 @@ export const MaspUnshield = ({ setAssetSelectorModalOpen, }: MaspUnshieldProps): JSX.Element => { // COMPONENT STATE + const [memo, setMemo] = useState(""); const [displayAmount, setDisplayAmount] = useAtom(transferAmountAtom); const [selectedAssetWithAmount, setSelectedAssetWithAmount] = useState< AssetWithAmountAndChain | undefined @@ -148,6 +149,8 @@ export const MaspUnshield = ({ address: destinationAddress, isShieldedAddress: false, onChangeAddress: setDestinationAddress, + memo, + onChangeMemo: setMemo, }} feeProps={feeProps} isSubmitting={isPerformingTransfer || isSuccess} diff --git a/apps/namadillo/src/App/Transfer/TransferDestination.tsx b/apps/namadillo/src/App/Transfer/TransferDestination.tsx index 8d6f9fb494..1e4ab46673 100644 --- a/apps/namadillo/src/App/Transfer/TransferDestination.tsx +++ b/apps/namadillo/src/App/Transfer/TransferDestination.tsx @@ -51,6 +51,8 @@ type TransferDestinationProps = { sourceAsset: Asset | undefined; onChangeAddress?: (address: Address) => void; onChangeMemo?: (address: string) => void; + isShielding?: boolean; + isUnshielding?: boolean; }; export const TransferDestination = ({ @@ -68,6 +70,8 @@ export const TransferDestination = ({ sourceAsset, onChangeAddress, onChangeMemo, + isShielding = false, + isUnshielding = false, }: TransferDestinationProps): JSX.Element => { const { data: accounts } = useAtomValue(allDefaultAccountsAtom); const [isModalOpen, setIsModalOpen] = useState(false); @@ -126,6 +130,12 @@ export const TransferDestination = ({ undefined : destinationAddress; + // Check if it's an internal shielding or unshielding transaction + const isInternalShieldingOrUnshielding = + (isShielding || isUnshielding) && + isNamadaAddress(sourceAddress ?? "") && + isNamadaAddress(destinationAddress ?? ""); + const sourceWallet = isNamadaAddress(destinationAddress || "") ? wallets.namada : wallets.keplr; const addressType = @@ -251,8 +261,7 @@ export const TransferDestination = ({ }
- - {customAddress && ( + {(customAddress || isInternalShieldingOrUnshielding) && ( diff --git a/apps/namadillo/src/App/Transfer/TransferModule.tsx b/apps/namadillo/src/App/Transfer/TransferModule.tsx index 05ddaf2236..0ee9ea9d46 100644 --- a/apps/namadillo/src/App/Transfer/TransferModule.tsx +++ b/apps/namadillo/src/App/Transfer/TransferModule.tsx @@ -227,6 +227,8 @@ export const TransferModule = ({ destinationAsset={selectedAsset?.asset} amount={source.amount} isSubmitting={isSubmitting} + isShielding={isShielding} + isUnshielding={isUnshielding} /> {ibcTransfer && requiresIbcChannels && ibcChannels && (