diff --git a/apps/extension/src/App/Common/NamCurrency.tsx b/apps/extension/src/App/Common/NamCurrency.tsx
index 10541bcad..ef8f119fa 100644
--- a/apps/extension/src/App/Common/NamCurrency.tsx
+++ b/apps/extension/src/App/Common/NamCurrency.tsx
@@ -8,9 +8,11 @@ type NamCurrencyProps = Omit<
export const NamCurrency = ({ ...props }: NamCurrencyProps): JSX.Element => {
return (
);
diff --git a/apps/namadillo/package.json b/apps/namadillo/package.json
index af441cf42..2339193f9 100644
--- a/apps/namadillo/package.json
+++ b/apps/namadillo/package.json
@@ -8,6 +8,7 @@
"private": true,
"dependencies": {
"@anomaorg/namada-indexer-client": "0.0.23",
+ "@chain-registry/client": "^1.48.80",
"@cosmjs/encoding": "^0.32.3",
"@tailwindcss/container-queries": "^0.1.1",
"@tanstack/query-core": "^5.40.0",
diff --git a/apps/namadillo/src/App/AccountOverview/AccountBalanceContainer.tsx b/apps/namadillo/src/App/AccountOverview/AccountBalanceContainer.tsx
index 89b336109..3f51fa127 100644
--- a/apps/namadillo/src/App/AccountOverview/AccountBalanceContainer.tsx
+++ b/apps/namadillo/src/App/AccountOverview/AccountBalanceContainer.tsx
@@ -47,7 +47,7 @@ export const AccountBalanceContainer = (): JSX.Element => {
)}
diff --git a/apps/namadillo/src/App/AccountOverview/NamBalanceContainer.tsx b/apps/namadillo/src/App/AccountOverview/NamBalanceContainer.tsx
index dbc26977a..bb381d962 100644
--- a/apps/namadillo/src/App/AccountOverview/NamBalanceContainer.tsx
+++ b/apps/namadillo/src/App/AccountOverview/NamBalanceContainer.tsx
@@ -29,7 +29,7 @@ const NamBalanceListItem = ({
);
@@ -50,7 +50,7 @@ export const NamBalanceContainer = (): JSX.Element => {
} = useBalances();
return (
-
+
);
diff --git a/apps/namadillo/src/App/Common/CurrencySelector.tsx b/apps/namadillo/src/App/Common/CurrencySelector.tsx
deleted file mode 100644
index 12af835ea..000000000
--- a/apps/namadillo/src/App/Common/CurrencySelector.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import { StyledSelectBox } from "@namada/components";
-import { CurrencyInfoListItem } from "@namada/utils";
-import clsx from "clsx";
-
-type CurrencySelectorProps = {
- value: string;
- onChange: (value: string) => void;
- currencies: CurrencyInfoListItem[];
-};
-
-export const CurrencySelector = ({
- value,
- onChange,
- currencies,
-}: CurrencySelectorProps): JSX.Element => {
- const getCurrencySymbol = (symbol: string): React.ReactNode => (
-
- {symbol}
-
- );
-
- return (
-
- onChange(e.target.value)}
- options={currencies.map((currency) => ({
- id: currency.id,
- value: (
- <>
- {getCurrencySymbol(currency.sign)}{" "}
- {currency.plural}
- >
- ),
- ariaLabel: currency.plural,
- }))}
- />
-
- );
-};
diff --git a/apps/namadillo/src/App/Common/FiatCurrency.tsx b/apps/namadillo/src/App/Common/FiatCurrency.tsx
deleted file mode 100644
index cc6742436..000000000
--- a/apps/namadillo/src/App/Common/FiatCurrency.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Currency, CurrencyProps } from "@namada/components";
-import { selectedCurrencyRateAtom } from "atoms/exchangeRates";
-import { selectedCurrencyAtom } from "atoms/settings";
-import BigNumber from "bignumber.js";
-import { useAtomValue } from "jotai";
-
-type FiatCurrencyProps = {
- amountInNam: BigNumber;
-} & Omit<
- CurrencyProps,
- "amount" | "currency" | "currencyPosition" | "spaceAroundSign"
->;
-
-export const FiatCurrency = ({
- amountInNam,
- ...props
-}: FiatCurrencyProps): JSX.Element => {
- const selectedFiatCurrency = useAtomValue(selectedCurrencyAtom);
- const selectedCurrencyRate = useAtomValue(selectedCurrencyRateAtom);
- return (
-
- );
-};
diff --git a/apps/namadillo/src/App/Common/NamCurrency.tsx b/apps/namadillo/src/App/Common/NamCurrency.tsx
index d6ff2df22..512005e7f 100644
--- a/apps/namadillo/src/App/Common/NamCurrency.tsx
+++ b/apps/namadillo/src/App/Common/NamCurrency.tsx
@@ -8,9 +8,9 @@ type NamCurrencyProps = Omit<
export const NamCurrency = ({ ...props }: NamCurrencyProps): JSX.Element => {
return (
diff --git a/apps/namadillo/src/App/Common/SelectModal.tsx b/apps/namadillo/src/App/Common/SelectModal.tsx
index 6e92dcc7f..448596d51 100644
--- a/apps/namadillo/src/App/Common/SelectModal.tsx
+++ b/apps/namadillo/src/App/Common/SelectModal.tsx
@@ -2,31 +2,39 @@ import { Modal } from "@namada/components";
import clsx from "clsx";
import React from "react";
import { IoClose } from "react-icons/io5";
+import { twMerge } from "tailwind-merge";
import { ModalTransition } from "./ModalTransition";
type SelectModalProps = {
children: React.ReactNode;
title: React.ReactNode;
onClose: () => void;
-};
+} & React.ComponentPropsWithoutRef<"div">;
export const SelectModal = ({
children,
title,
onClose,
+ className,
+ ...props
}: SelectModalProps): JSX.Element => {
return (
-
+
{title}
diff --git a/apps/namadillo/src/App/Common/TabSelector.tsx b/apps/namadillo/src/App/Common/TabSelector.tsx
index 29e0eea6b..b25c5864f 100644
--- a/apps/namadillo/src/App/Common/TabSelector.tsx
+++ b/apps/namadillo/src/App/Common/TabSelector.tsx
@@ -18,25 +18,25 @@ export const TabSelector = ({
onChange,
}: TabSelectorProps): JSX.Element => {
return (
-
+
+ {items.map((item) => (
+ -
+
+
+ ))}
+
);
};
diff --git a/apps/namadillo/src/App/Common/TokenCurrency.tsx b/apps/namadillo/src/App/Common/TokenCurrency.tsx
new file mode 100644
index 000000000..18f0c921f
--- /dev/null
+++ b/apps/namadillo/src/App/Common/TokenCurrency.tsx
@@ -0,0 +1,23 @@
+import { Asset } from "@chain-registry/types";
+import { Currency, CurrencyProps } from "@namada/components";
+
+type TokenCurrencyProps = Omit<
+ CurrencyProps,
+ "currency" | "currencyPosition" | "spaceAroundSign"
+> & { asset: Asset };
+
+export const TokenCurrency = ({
+ asset,
+ ...props
+}: TokenCurrencyProps): JSX.Element => {
+ return (
+
+ );
+};
diff --git a/apps/namadillo/src/App/Settings/CurrencySelector.tsx b/apps/namadillo/src/App/Settings/CurrencySelector.tsx
deleted file mode 100644
index dae7b4e4b..000000000
--- a/apps/namadillo/src/App/Settings/CurrencySelector.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Stack } from "@namada/components";
-import { FiatCurrencyList } from "@namada/utils";
-import { selectedCurrencyAtom } from "atoms/settings";
-import { useAtom } from "jotai";
-import { CurrencySelectorEntry } from "./CurrencySelectorEntry";
-
-export const CurrencySelector = (): JSX.Element => {
- const [selectedCurrency, setSelectedCurrency] = useAtom(selectedCurrencyAtom);
- return (
-
- {FiatCurrencyList.map((currency) => (
- setSelectedCurrency(currency.id)}
- />
- ))}
-
- );
-};
diff --git a/apps/namadillo/src/App/Settings/CurrencySelectorEntry.tsx b/apps/namadillo/src/App/Settings/CurrencySelectorEntry.tsx
deleted file mode 100644
index 343395ce0..000000000
--- a/apps/namadillo/src/App/Settings/CurrencySelectorEntry.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { CurrencyInfoListItem } from "@namada/utils";
-import clsx from "clsx";
-import { twMerge } from "tailwind-merge";
-
-type CurrencySelectorEntryType = {
- currency: CurrencyInfoListItem;
- selected: boolean;
- onClick: () => void;
-};
-
-export const CurrencySelectorEntry = ({
- currency,
- selected,
- onClick,
-}: CurrencySelectorEntryType): JSX.Element => {
- return (
-
-
-
- );
-};
diff --git a/apps/namadillo/src/App/Staking/BondingAmountOverview.tsx b/apps/namadillo/src/App/Staking/BondingAmountOverview.tsx
index b21debafa..04c9130b7 100644
--- a/apps/namadillo/src/App/Staking/BondingAmountOverview.tsx
+++ b/apps/namadillo/src/App/Staking/BondingAmountOverview.tsx
@@ -40,7 +40,7 @@ export const BondingAmountOverview = ({
className={clsx("text-2xl", {
[updatedValueClassList]: hasUpdatedValue,
})}
- currencySignClassName="text-lg"
+ currencySymbolClassName="text-lg"
/>
{amountToDelegate && amountToDelegate.gt(0) && (
diff --git a/apps/namadillo/src/App/Staking/StakingRewardsPanel.tsx b/apps/namadillo/src/App/Staking/StakingRewardsPanel.tsx
index 853a3a1e5..b78d34763 100644
--- a/apps/namadillo/src/App/Staking/StakingRewardsPanel.tsx
+++ b/apps/namadillo/src/App/Staking/StakingRewardsPanel.tsx
@@ -39,7 +39,7 @@ export const StakingRewardsPanel = (): JSX.Element => {
}
callToAction={
diff --git a/apps/namadillo/src/App/Staking/StakingSummary.tsx b/apps/namadillo/src/App/Staking/StakingSummary.tsx
index d33061aa3..8bc3f7a42 100644
--- a/apps/namadillo/src/App/Staking/StakingSummary.tsx
+++ b/apps/namadillo/src/App/Staking/StakingSummary.tsx
@@ -66,7 +66,7 @@ export const StakingSummary = (): JSX.Element => {
}
callToAction={
diff --git a/apps/namadillo/src/App/Transfer/AssetCard.tsx b/apps/namadillo/src/App/Transfer/AssetCard.tsx
new file mode 100644
index 000000000..ced7a5cbd
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/AssetCard.tsx
@@ -0,0 +1,31 @@
+import { Asset } from "@chain-registry/types";
+import clsx from "clsx";
+
+type AssetCardProps = {
+ asset: Asset;
+};
+
+export const AssetCard = ({ asset }: AssetCardProps): JSX.Element => {
+ const image = asset.logo_URIs?.svg || asset.logo_URIs?.png;
+ return (
+
+ {image ?
+
+ :
+ }
+ {asset.name}
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/AvailableAmountFooter.tsx b/apps/namadillo/src/App/Transfer/AvailableAmountFooter.tsx
index e4a50c47a..9c05afb27 100644
--- a/apps/namadillo/src/App/Transfer/AvailableAmountFooter.tsx
+++ b/apps/namadillo/src/App/Transfer/AvailableAmountFooter.tsx
@@ -1,20 +1,21 @@
-import { ActionButton, Currency } from "@namada/components";
-import { KnownCurrencies } from "@namada/utils";
+import { Asset } from "@chain-registry/types";
+import { ActionButton } from "@namada/components";
+import { TokenCurrency } from "App/Common/TokenCurrency";
import BigNumber from "bignumber.js";
import clsx from "clsx";
type AvailableAmountFooterProps = {
availableAmount?: BigNumber;
- currency?: keyof typeof KnownCurrencies;
+ asset?: Asset;
onClickMax?: () => void;
};
export const AvailableAmountFooter = ({
availableAmount,
- currency,
+ asset,
onClickMax,
}: AvailableAmountFooterProps): JSX.Element => {
- if (!currency || availableAmount === undefined) {
+ if (availableAmount === undefined || !asset) {
return <>>;
}
@@ -26,12 +27,7 @@ export const AvailableAmountFooter = ({
>
Available:
-
+
{onClickMax && (
diff --git a/apps/namadillo/src/App/Transfer/ChainCard.tsx b/apps/namadillo/src/App/Transfer/ChainCard.tsx
index e69de29bb..8d76014ce 100644
--- a/apps/namadillo/src/App/Transfer/ChainCard.tsx
+++ b/apps/namadillo/src/App/Transfer/ChainCard.tsx
@@ -0,0 +1,19 @@
+import { Chain } from "@chain-registry/types";
+import clsx from "clsx";
+
+type ChainCardProps = {
+ chain: Chain;
+};
+
+export const ChainCard = ({ chain }: ChainCardProps): JSX.Element => {
+ return (
+
+
+ {chain.pretty_name}
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/ConnectProviderButton.tsx b/apps/namadillo/src/App/Transfer/ConnectProviderButton.tsx
index b5935dd22..211cee4c5 100644
--- a/apps/namadillo/src/App/Transfer/ConnectProviderButton.tsx
+++ b/apps/namadillo/src/App/Transfer/ConnectProviderButton.tsx
@@ -10,7 +10,7 @@ export const ConnectProviderButton = ({
return (
{
+ const [selectedWallet, setWallet] = useAtom(selectedIBCWallet);
+ const [chainId, setChainId] = useAtom(selectedIBCChainAtom);
+ const [selectedAsset, setSelectedAsset] = useState();
+ const [isShielded, setShielded] = useState(true);
+
+ const sourceChainConfig: [Chain, Asset[]][] = [
+ [cosmos, cosmosAssets.assets],
+ [osmosis, osmosisAssets.assets],
+ [celestia, celestiaAssets.assets],
+ [dydx, dydxAssets.assets],
+ [stride, strideAssets.assets],
+ [stargaze, stargazeAssets.assets],
+ ];
+
+ const sourceChains: Record = useMemo(() => {
+ return sourceChainConfig.reduce((prev, current) => {
+ return {
+ ...prev,
+ [current[0].chain_id]: current[0],
+ };
+ }, {});
+ }, []);
+
+ const sourceAssetList: Asset[] | undefined = useMemo(() => {
+ if (!chainId) return;
+ const config = sourceChainConfig.find(
+ (config) => config[0].chain_id === chainId
+ );
+ if (config) {
+ return config[1];
+ }
+ }, [chainId]);
+
+ const selectedSourceChain =
+ chainId && chainId in sourceChains ? sourceChains[chainId] : undefined;
+
+ useEffect(() => {
+ const config = sourceChainConfig.find(
+ (config) => config[0].chain_id === chainId
+ );
+
+ if (config) {
+ setSelectedAsset(config[1][0]);
+ }
+ }, [chainId]);
+
+ return (
+
+ {}}
+ availableWallets={Object.values(wallets)}
+ sourceWallet={selectedWallet ? wallets[selectedWallet] : undefined}
+ onChangeWallet={async (wallet: WalletProvider) => {
+ try {
+ await integrations[wallet.id].connect();
+ setWallet(wallet.id);
+ if (!chainId) {
+ setChainId(cosmos.chain_id);
+ }
+ } catch (err) {
+ console.error(err);
+ }
+ }}
+ onChangeSourceChain={(chain) => {
+ setChainId(chain.chain_id);
+ }}
+ onChangeSelectedAsset={setSelectedAsset}
+ availableSourceChains={Object.values(sourceChains)}
+ availableAssets={sourceAssetList}
+ selectedAsset={selectedAsset}
+ sourceChain={selectedSourceChain}
+ destinationChain={namadaChain as Chain}
+ destinationWallet={wallets.namada}
+ isShielded={isShielded}
+ onChangeShielded={setShielded}
+ availableAmount={new BigNumber(100) /* Change this */}
+ transactionFee={new BigNumber(0.01)}
+ />
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/IBCFromNamadaModule.tsx b/apps/namadillo/src/App/Transfer/IBCFromNamadaModule.tsx
deleted file mode 100644
index 088f5a72c..000000000
--- a/apps/namadillo/src/App/Transfer/IBCFromNamadaModule.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import { TransferModule } from "./TransferModule";
-
-export const IBCFromNamadaModule = (): JSX.Element => {
- return (
-
- {}} />
-
- );
-};
diff --git a/apps/namadillo/src/App/Transfer/IBCTransfers.tsx b/apps/namadillo/src/App/Transfer/IBCTransfers.tsx
deleted file mode 100644
index 3a551ec52..000000000
--- a/apps/namadillo/src/App/Transfer/IBCTransfers.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Panel } from "@namada/components";
-import { IBCFromNamadaModule } from "./IBCFromNamadaModule";
-
-export const IBCTransfers = (): JSX.Element => {
- return (
-
- );
-};
diff --git a/apps/namadillo/src/App/Transfer/SelectAssetModal.tsx b/apps/namadillo/src/App/Transfer/SelectAssetModal.tsx
new file mode 100644
index 000000000..4dbb76ea6
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/SelectAssetModal.tsx
@@ -0,0 +1,61 @@
+import { Asset } from "@chain-registry/types";
+import { Stack } from "@namada/components";
+import { Search } from "App/Common/Search";
+import { SelectModal } from "App/Common/SelectModal";
+import clsx from "clsx";
+import { useMemo, useState } from "react";
+import { twMerge } from "tailwind-merge";
+import { AssetCard } from "./AssetCard";
+
+type SelectWalletModalProps = {
+ onClose: () => void;
+ onSelect: (asset: Asset) => void;
+ assets: Asset[];
+};
+
+export const SelectAssetModal = ({
+ onClose,
+ onSelect,
+ assets,
+}: SelectWalletModalProps): JSX.Element => {
+ const [filter, setFilter] = useState("");
+
+ const filteredAssets = useMemo(() => {
+ return assets.filter(
+ (asset) =>
+ asset.name.indexOf(filter) >= 0 || asset.symbol.indexOf(filter) >= 0
+ );
+ }, [assets, filter]);
+
+ return (
+
+
+
+
+
+ {filteredAssets.map((asset: Asset, index: number) => (
+
+
+
+ ))}
+
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/SelectChainModal.tsx b/apps/namadillo/src/App/Transfer/SelectChainModal.tsx
index 61fed101b..48056abd0 100644
--- a/apps/namadillo/src/App/Transfer/SelectChainModal.tsx
+++ b/apps/namadillo/src/App/Transfer/SelectChainModal.tsx
@@ -1,34 +1,61 @@
+import { Chain, Chains } from "@chain-registry/types";
import { Stack } from "@namada/components";
+import { Search } from "App/Common/Search";
import { SelectModal } from "App/Common/SelectModal";
import clsx from "clsx";
-import { Chain } from "types";
+import { useMemo, useState } from "react";
+import { twMerge } from "tailwind-merge";
+import { ChainCard } from "./ChainCard";
type SelectChainModalProps = {
onClose: () => void;
- chains: Chain[];
+ onSelect: (chain: Chain) => void;
+ chains: Chains;
};
export const SelectChainModal = ({
onClose,
+ onSelect,
chains,
}: SelectChainModalProps): JSX.Element => {
+ const [filter, setFilter] = useState("");
+
+ const filteredChains = useMemo(() => {
+ return chains.filter((chain) => chain.pretty_name.indexOf(filter) >= 0);
+ }, [chains, filter]);
+
return (
-
- {chains.map((chain) => (
-
-
-
- ))}
-
+
+
+
+ {filteredChains.length > 0 && (
+
+ {filteredChains.map((chain) => (
+
+
+
+ ))}
+
+ )}
+ {filteredChains.length === 0 && There are no available chains
}
);
};
diff --git a/apps/namadillo/src/App/Transfer/SelectProviderModal.tsx b/apps/namadillo/src/App/Transfer/SelectProviderModal.tsx
deleted file mode 100644
index 88fcb4eb7..000000000
--- a/apps/namadillo/src/App/Transfer/SelectProviderModal.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import { SelectModal } from "App/Common/SelectModal";
-import { Provider } from "types";
-
-type SelectProviderModalProps = {
- onClose: () => void;
- providers: Provider[];
- onConnect: (provider: Provider) => void;
-};
-
-export const SelectProviderModal = ({
- onClose,
- onConnect,
- providers,
-}: SelectProviderModalProps): JSX.Element => {
- return (
-
-
- {providers.map((provider: Provider, index) => (
- -
-
-
- ))}
-
-
- );
-};
diff --git a/apps/namadillo/src/App/Transfer/SelectWalletModal.tsx b/apps/namadillo/src/App/Transfer/SelectWalletModal.tsx
new file mode 100644
index 000000000..4011dac83
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/SelectWalletModal.tsx
@@ -0,0 +1,46 @@
+import { integrations } from "@namada/integrations";
+import { SelectModal } from "App/Common/SelectModal";
+import { WalletProvider } from "types";
+import { WalletCard } from "./WalletCard";
+
+type SelectWalletModalProps = {
+ onClose: () => void;
+ wallets: WalletProvider[];
+ onConnect: (wallet: WalletProvider) => void;
+};
+
+export const SelectWalletModal = ({
+ onClose,
+ onConnect,
+ wallets,
+}: SelectWalletModalProps): JSX.Element => {
+ const isConnected = (_wallet: WalletProvider): boolean => {
+ return false;
+ };
+
+ const isInstalled = (wallet: WalletProvider): boolean => {
+ if (wallet.id in integrations) {
+ return integrations[wallet.id].detect();
+ }
+ return false;
+ };
+
+ return (
+
+
+ {wallets
+ .filter((wallet) => wallet.id !== "namada")
+ .map((wallet: WalletProvider, index) => (
+ -
+ onConnect(wallet)}
+ />
+
+ ))}
+
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/SelectedAsset.tsx b/apps/namadillo/src/App/Transfer/SelectedAsset.tsx
index 4f86e205b..ca101a544 100644
--- a/apps/namadillo/src/App/Transfer/SelectedAsset.tsx
+++ b/apps/namadillo/src/App/Transfer/SelectedAsset.tsx
@@ -1,6 +1,6 @@
+import { Asset, Chain } from "@chain-registry/types";
import clsx from "clsx";
import { GoChevronDown } from "react-icons/go";
-import { Asset, Chain } from "types";
import { EmptyResourceIcon } from "./EmptyResourceIcon";
type SelectedAssetProps = {
@@ -15,14 +15,18 @@ export const SelectedAsset = ({
onClick,
}: SelectedAssetProps): JSX.Element => {
const selectorClassList = clsx(
- `flex items-center gap-2.5 text-lg text-white font-light cursor-pointer uppercase`
+ `flex items-center gap-4 text-xl text-white font-light cursor-pointer uppercase`
);
+ const isDisabled = !chain;
+
return (
diff --git a/apps/namadillo/src/App/Transfer/SelectedChain.tsx b/apps/namadillo/src/App/Transfer/SelectedChain.tsx
index c2f923d0e..619fa4df2 100644
--- a/apps/namadillo/src/App/Transfer/SelectedChain.tsx
+++ b/apps/namadillo/src/App/Transfer/SelectedChain.tsx
@@ -1,51 +1,61 @@
+import { Chain } from "@chain-registry/types";
import clsx from "clsx";
import { GoChevronDown } from "react-icons/go";
-import { Chain, Provider } from "types";
+import { WalletProvider } from "types";
+
import { EmptyResourceIcon } from "./EmptyResourceIcon";
type SelectedChainProps = {
chain?: Chain;
- provider?: Provider;
+ wallet?: WalletProvider;
onClick?: () => void;
+ iconSize?: string;
};
export const SelectedChain = ({
chain,
- provider,
+ wallet,
onClick,
+ iconSize,
}: SelectedChainProps): JSX.Element => {
const selectorClassList = clsx(
- `flex items-center gap-2.5 text-white font-light cursor-pointer`
+ `flex items-center gap-2.5 text-white font-light`,
+ { "cursor-auto": !onClick }
);
+ const isDisabled = !wallet;
+
return (
diff --git a/apps/namadillo/src/App/Transfer/SelectedWallet.tsx b/apps/namadillo/src/App/Transfer/SelectedWallet.tsx
new file mode 100644
index 000000000..d73d8189f
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/SelectedWallet.tsx
@@ -0,0 +1,61 @@
+import { integrations } from "@namada/integrations";
+import { shortenAddress } from "@namada/utils";
+import clsx from "clsx";
+import { useEffect, useState } from "react";
+import { WalletProvider } from "types";
+
+type SelectedWalletProps = {
+ wallet: WalletProvider;
+ onClick?: () => void;
+ isShielded?: boolean;
+};
+
+export const SelectedWallet = ({
+ wallet,
+ onClick,
+ isShielded,
+}: SelectedWalletProps): JSX.Element => {
+ const [walletAddress, setWalletAddress] = useState("");
+
+ const loadAccounts = async (): Promise => {
+ try {
+ const integration = integrations[wallet.id];
+ integration.detect();
+ await integration.connect();
+ const accounts = await integration.accounts();
+
+ if (accounts && accounts.length > 0) {
+ if (wallet.id === "namada" && isShielded && accounts.length > 1) {
+ setWalletAddress(accounts[1].address);
+ return;
+ }
+ setWalletAddress(accounts[0].address);
+ }
+ } catch {
+ // TODO: handle error catching
+ }
+ };
+
+ useEffect(() => {
+ loadAccounts();
+ }, [isShielded]);
+
+ return (
+
+ {walletAddress && shortenAddress(walletAddress, 8, 6)}
+
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/Transfer.tsx b/apps/namadillo/src/App/Transfer/Transfer.tsx
index 26bc34d25..b46002c5d 100644
--- a/apps/namadillo/src/App/Transfer/Transfer.tsx
+++ b/apps/namadillo/src/App/Transfer/Transfer.tsx
@@ -1,5 +1,5 @@
import { Route, Routes } from "react-router-dom";
-import { IBCTransfers } from "./IBCTransfers";
+import { Example } from "./Example";
import { NamTransfer } from "./NamTransfer";
import { Shield } from "./Shield";
import { ShieldAll } from "./ShieldAll";
@@ -17,10 +17,7 @@ export const Transfer: React.FC = () => (
path={TransferRoutes.shieldAll().toString()}
element={}
/>
- }
- />
+ } />
);
diff --git a/apps/namadillo/src/App/Transfer/TransferArrow.tsx b/apps/namadillo/src/App/Transfer/TransferArrow.tsx
new file mode 100644
index 000000000..ff7158087
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/TransferArrow.tsx
@@ -0,0 +1,26 @@
+type TransferArrowProps = {
+ color: string;
+};
+
+export const TransferArrow = ({ color }: TransferArrowProps): JSX.Element => (
+
+);
diff --git a/apps/namadillo/src/App/Transfer/TransferDestination.tsx b/apps/namadillo/src/App/Transfer/TransferDestination.tsx
index a9f454622..47c29d66f 100644
--- a/apps/namadillo/src/App/Transfer/TransferDestination.tsx
+++ b/apps/namadillo/src/App/Transfer/TransferDestination.tsx
@@ -1,21 +1,24 @@
+import { Chain } from "@chain-registry/types";
import { NamCurrency } from "App/Common/NamCurrency";
import { TabSelector } from "App/Common/TabSelector";
import BigNumber from "bignumber.js";
import clsx from "clsx";
-import { Chain, Provider } from "types";
+import { WalletProvider } from "types";
import namadaShieldedSvg from "./assets/namada-shielded.svg";
import namadaTransparentSvg from "./assets/namada-transparent.svg";
import { CustomAddressForm } from "./CustomAddressForm";
import { SelectedChain } from "./SelectedChain";
+import { SelectedWallet } from "./SelectedWallet";
type TransferDestinationProps = {
isShielded?: boolean;
onChangeShielded?: (isShielded: boolean) => void;
chain?: Chain;
- provider?: Provider;
+ wallet?: WalletProvider;
className?: string;
transactionFee?: BigNumber;
customAddressActive?: boolean;
+ openChainSelector?: () => void;
onToggleCustomAddress?: (isActive: boolean) => void;
onChangeAddress?: (address: string | undefined) => void;
address?: string;
@@ -27,19 +30,22 @@ const parseChainInfo = (
chain?: Chain,
isShielded?: boolean
): Chain | undefined => {
- if (chain?.name !== "Namada") {
+ if (chain?.chain_name !== "namada") {
return chain;
}
return {
...chain,
- name: isShielded ? "Namada Shielded" : "Namada Transparent",
- iconUrl: isShielded ? namadaShieldedSvg : namadaTransparentSvg,
+ pretty_name: isShielded ? "Namada Shielded" : "Namada Transparent",
+ logo_URIs: {
+ ...chain.logo_URIs,
+ svg: isShielded ? namadaShieldedSvg : namadaTransparentSvg,
+ },
};
};
export const TransferDestination = ({
chain,
- provider,
+ wallet,
isShielded,
onChangeShielded,
transactionFee,
@@ -49,22 +55,31 @@ export const TransferDestination = ({
onChangeAddress,
memo,
onChangeMemo,
+ openChainSelector,
}: TransferDestinationProps): JSX.Element => {
return (
- {onChangeShielded && chain?.name === "Namada" && (
-
onChangeShielded(!isShielded)}
- />
+ {onChangeShielded && chain?.chain_name === "namada" && (
+
)}
{onToggleCustomAddress && (
@@ -78,10 +93,15 @@ export const TransferDestination = ({
/>
)}
-
+
+
+ {wallet && }
+
{customAddressActive && (
+
diff --git a/apps/namadillo/src/App/Transfer/TransferModule.tsx b/apps/namadillo/src/App/Transfer/TransferModule.tsx
index 04d198e97..35658dc7f 100644
--- a/apps/namadillo/src/App/Transfer/TransferModule.tsx
+++ b/apps/namadillo/src/App/Transfer/TransferModule.tsx
@@ -1,63 +1,110 @@
-import { Stack } from "@namada/components";
+import { Asset, Chain, Chains } from "@chain-registry/types";
+import { ActionButton, Stack } from "@namada/components";
import BigNumber from "bignumber.js";
import { useState } from "react";
-import { Asset, Chain } from "types";
-import { SelectProviderModal } from "./SelectProviderModal";
+import { WalletProvider } from "types";
+import { SelectAssetModal } from "./SelectAssetModal";
+import { SelectChainModal } from "./SelectChainModal";
+import { SelectWalletModal } from "./SelectWalletModal";
+import { TransferArrow } from "./TransferArrow";
import { TransferDestination } from "./TransferDestination";
import { TransferSource } from "./TransferSource";
type TransferModuleProps = {
isConnected: boolean;
+ availableAmount?: BigNumber;
+ availableWallets: WalletProvider[];
+ onSubmitTransfer: () => void;
+ onChangeWallet?: (wallet: WalletProvider) => void;
+ sourceWallet?: WalletProvider;
+ availableSourceChains?: Chains;
sourceChain?: Chain;
- onChangeSourceChain?: () => void;
+ onChangeSourceChain?: (chain: Chain) => void;
+ availableDestinationChains?: Chains;
destinationChain?: Chain;
+ destinationWallet?: WalletProvider;
onChangeDestinationChain?: (chain: Chain) => void;
selectedAsset?: Asset;
+ availableAssets?: Asset[];
onChangeSelectedAsset?: (asset: Asset | undefined) => void;
- amount?: BigNumber;
- onChangeAmount?: (amount: BigNumber) => void;
isShielded?: boolean;
onChangeShielded?: (isShielded: boolean) => void;
enableCustomAddress?: boolean;
- onSubmitTransfer: () => void;
+ transactionFee?: BigNumber;
};
export const TransferModule = ({
isConnected,
selectedAsset,
+ availableAssets,
+ onChangeSelectedAsset,
+ availableSourceChains,
sourceChain,
+ onChangeSourceChain,
+ availableDestinationChains,
destinationChain,
+ destinationWallet,
+ onChangeDestinationChain,
isShielded,
onChangeShielded,
enableCustomAddress,
+ onChangeWallet,
+ availableWallets,
+ sourceWallet,
+ availableAmount,
+ transactionFee,
}: TransferModuleProps): JSX.Element => {
const [providerSelectorModalOpen, setProviderSelectorModalOpen] =
useState(false);
- const [chainSelectorModalOpen, setChainSelectorModalOpen] = useState(false);
+ const [sourceChainModalOpen, setSourceChainModalOpen] = useState(false);
+ const [destinationChainModalOpen, setDestinationChainModalOpen] =
+ useState(false);
const [assetSelectorModalOpen, setAssetSelectorModalOpen] = useState(false);
const [customAddressActive, setCustomAddressActive] = useState(false);
const [memo, setMemo] = useState("");
const [customAddress, setCustomAddress] = useState("");
- const [amount, setAmount] = useState(new BigNumber(0));
+ const [amount, setAmount] = useState(new BigNumber(0));
+
+ const validateTransfer = (): boolean => {
+ if (!amount || amount.eq(0)) return false;
+ if (!sourceWallet || !sourceChain || !selectedAsset) return false;
+ if (!destinationWallet || !destinationChain) return false;
+ if (
+ !availableAmount ||
+ availableAmount.lt(amount.plus(transactionFee || 0))
+ ) {
+ return false;
+ }
+ return true;
+ };
+
+ const onSubmit = (e: React.FormEvent): void => {
+ // TODO: implement submit
+ e.preventDefault();
+ };
return (
<>
-
+
setProviderSelectorModalOpen(true)}
- openChainSelector={() => setChainSelectorModalOpen(true)}
+ openChainSelector={() => setSourceChainModalOpen(true)}
openAssetSelector={() => setAssetSelectorModalOpen(true)}
amount={amount}
- onChangeAmount={(e) =>
- setAmount(e.target.value || new BigNumber(0))
- }
+ availableAmount={availableAmount}
+ onChangeAmount={setAmount}
/>
+
+
+
- {chainSelectorModalOpen && }
- {assetSelectorModalOpen && }
+
+ {sourceWallet ? "Submit" : "Select Wallet"}
+
- {providerSelectorModalOpen && (
- setProviderSelectorModalOpen(false)}
- onConnect={() => {}}
+ onConnect={onChangeWallet}
+ />
+ )}
+ {sourceChainModalOpen && onChangeSourceChain && sourceWallet && (
+ setSourceChainModalOpen(false)}
+ chains={availableSourceChains || []}
+ onSelect={onChangeSourceChain}
+ />
+ )}
+ {destinationChainModalOpen &&
+ onChangeDestinationChain &&
+ destinationWallet && (
+ setDestinationChainModalOpen(false)}
+ chains={availableDestinationChains || []}
+ onSelect={onChangeDestinationChain}
+ />
+ )}
+ {assetSelectorModalOpen && onChangeSelectedAsset && sourceWallet && (
+ setAssetSelectorModalOpen(false)}
+ assets={availableAssets || []}
+ onSelect={onChangeSelectedAsset}
/>
)}
>
diff --git a/apps/namadillo/src/App/Transfer/TransferSource.tsx b/apps/namadillo/src/App/Transfer/TransferSource.tsx
index 3e52ca813..2b2b76dfd 100644
--- a/apps/namadillo/src/App/Transfer/TransferSource.tsx
+++ b/apps/namadillo/src/App/Transfer/TransferSource.tsx
@@ -1,45 +1,52 @@
-import { AmountInput, ChangeAmountEvent } from "@namada/components";
+import { Asset, Chain } from "@chain-registry/types";
+import { AmountInput } from "@namada/components";
import BigNumber from "bignumber.js";
import clsx from "clsx";
-import { Asset, Chain, Provider } from "types";
+import { WalletProvider } from "types";
import { AvailableAmountFooter } from "./AvailableAmountFooter";
import { ConnectProviderButton } from "./ConnectProviderButton";
import { SelectedAsset } from "./SelectedAsset";
import { SelectedChain } from "./SelectedChain";
+import { SelectedWallet } from "./SelectedWallet";
export type TransferSourceProps = {
isConnected: boolean;
- provider?: Provider;
+ wallet?: WalletProvider;
asset?: Asset;
chain?: Chain;
openChainSelector?: () => void;
openAssetSelector?: () => void;
openProviderSelector?: () => void;
amount?: BigNumber;
- onChangeAmount?: ChangeAmountEvent;
+ availableAmount?: BigNumber;
+ onChangeAmount?: (amount: BigNumber | undefined) => void;
};
export const TransferSource = ({
chain,
asset,
- provider,
+ wallet,
openProviderSelector,
openChainSelector,
openAssetSelector,
amount,
+ availableAmount,
onChangeAmount,
}: TransferSourceProps): JSX.Element => {
return (
);
};
diff --git a/apps/namadillo/src/App/Transfer/WalletCard.tsx b/apps/namadillo/src/App/Transfer/WalletCard.tsx
new file mode 100644
index 000000000..931e6973b
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/WalletCard.tsx
@@ -0,0 +1,50 @@
+import { GoLinkExternal } from "react-icons/go";
+import { WalletProvider } from "types";
+
+type WalletCardProps = {
+ wallet: WalletProvider;
+ installed: boolean;
+ connected: boolean;
+ onConnect?: () => void;
+ onSelect?: () => void;
+};
+
+export const WalletCard = ({
+ wallet,
+ installed,
+ connected,
+ onConnect,
+ onSelect,
+}: WalletCardProps): JSX.Element => {
+ const getDownloadUrl = (): string => {
+ if (/firefox/i.test(navigator.userAgent)) {
+ return wallet.downloadUrl.firefox;
+ }
+ return wallet.downloadUrl.chrome;
+ };
+
+ return (
+
+
+
+ {wallet.name}
+
+
+ {!installed && (
+
+ Install
+
+ )}
+ {installed && !connected && (
+
+ )}
+ {installed && connected && }
+
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/__mocks__/assets.tsx b/apps/namadillo/src/App/Transfer/__mocks__/assets.tsx
new file mode 100644
index 000000000..bbd2c859d
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/__mocks__/assets.tsx
@@ -0,0 +1,19 @@
+import { Asset } from "@chain-registry/types";
+
+export const assetMock: Partial = {
+ name: "Ethereum",
+ symbol: "ETH",
+ logo_URIs: {
+ svg: "https://example.com/eth-icon.png",
+ },
+};
+
+export const assetMock2: Partial = {
+ name: "Bitcoin",
+ symbol: "BTC",
+ logo_URIs: { svg: "btc.svg" },
+};
+
+export const assetMockList: Array> = [assetMock, assetMock2];
+
+export const assetWithoutLogo: Partial = { ...assetMock, logo_URIs: {} };
diff --git a/apps/namadillo/src/App/Transfer/__mocks__/chains.ts b/apps/namadillo/src/App/Transfer/__mocks__/chains.ts
index da2ad5502..cb3b49c35 100644
--- a/apps/namadillo/src/App/Transfer/__mocks__/chains.ts
+++ b/apps/namadillo/src/App/Transfer/__mocks__/chains.ts
@@ -1,13 +1,15 @@
-import { Chain } from "types";
+import { Chain } from "@chain-registry/types";
-export const namadaChainMock: Chain = {
- chainId: "test",
- name: "Namada",
- iconUrl: "namada-icon",
+export const namadaChainMock: Partial = {
+ chain_id: "namada-mock-chain-id",
+ chain_name: "namada",
+ pretty_name: "Namada",
+ logo_URIs: { svg: "namada-icon" },
};
-export const randomChainMock: Chain = {
- chainId: "test",
- name: "TestChain",
- iconUrl: "testchain-icon",
+export const randomChainMock: Partial = {
+ chain_id: "testchain-mock-chain-id",
+ chain_name: "testchain",
+ pretty_name: "TestChain",
+ logo_URIs: { svg: "testchain-icon" },
};
diff --git a/apps/namadillo/src/App/Transfer/__mocks__/providers.ts b/apps/namadillo/src/App/Transfer/__mocks__/providers.ts
index af2cd0157..32d34c5cd 100644
--- a/apps/namadillo/src/App/Transfer/__mocks__/providers.ts
+++ b/apps/namadillo/src/App/Transfer/__mocks__/providers.ts
@@ -1,12 +1,11 @@
-import { Provider } from "types";
+import { WalletProvider } from "types";
-export const providerMock: Provider = {
+export const walletMock: WalletProvider = {
+ id: "keplr",
name: "Keplr",
iconUrl: "test.svg",
- connected: false,
-};
-
-export const providerConnectedMock: Provider = {
- ...providerMock,
- connected: true,
+ downloadUrl: {
+ chrome: "https://google.com",
+ firefox: "https://mozilla.org",
+ },
};
diff --git a/apps/namadillo/src/App/Transfer/__tests__/AssetCard.test.tsx b/apps/namadillo/src/App/Transfer/__tests__/AssetCard.test.tsx
new file mode 100644
index 000000000..813bfd155
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/__tests__/AssetCard.test.tsx
@@ -0,0 +1,23 @@
+import { Asset } from "@chain-registry/types";
+import { render, screen } from "@testing-library/react";
+import { AssetCard } from "App/Transfer/AssetCard";
+import { assetMock, assetWithoutLogo } from "App/Transfer/__mocks__/assets";
+
+describe("Component: AssetCard", () => {
+ it("should render asset name", () => {
+ render();
+ expect(screen.getByText("Ethereum")).toBeInTheDocument();
+ });
+
+ it("should render asset logo if available", () => {
+ render();
+ const logo = screen.getByAltText("Ethereum logo");
+ expect(logo).toBeInTheDocument();
+ expect(logo).toHaveAttribute("src", assetMock.logo_URIs?.svg);
+ });
+
+ it("should render placeholder if logo is not available", () => {
+ render();
+ expect(screen.getByAltText(/logo not available/i)).toBeInTheDocument();
+ });
+});
diff --git a/apps/namadillo/src/App/Transfer/__tests__/AvailableAmountFooter.test.tsx b/apps/namadillo/src/App/Transfer/__tests__/AvailableAmountFooter.test.tsx
index 088ff37b3..f949ee1cc 100644
--- a/apps/namadillo/src/App/Transfer/__tests__/AvailableAmountFooter.test.tsx
+++ b/apps/namadillo/src/App/Transfer/__tests__/AvailableAmountFooter.test.tsx
@@ -1,4 +1,6 @@
+import { Asset } from "@chain-registry/types";
import { fireEvent, render, screen } from "@testing-library/react";
+import { assetMock } from "App/Transfer/__mocks__/assets";
import BigNumber from "bignumber.js";
import { AvailableAmountFooter } from "../AvailableAmountFooter";
@@ -12,14 +14,14 @@ describe("Component: AvailableAmountFooter", () => {
const callback = jest.fn();
render(
);
const amount = screen.getByText("1,234");
const button = screen.getByRole("button");
- expect(amount.parentNode?.textContent).toContain("1,234.456 NAM");
+ expect(amount.parentNode?.textContent).toContain("1,234.456 ETH");
expect(button).toBeEnabled();
fireEvent.click(button);
expect(callback).toHaveBeenCalledTimes(1);
@@ -29,7 +31,7 @@ describe("Component: AvailableAmountFooter", () => {
render(
);
expect(screen.queryByRole("button")).not.toBeInTheDocument();
@@ -40,7 +42,7 @@ describe("Component: AvailableAmountFooter", () => {
render(
);
diff --git a/apps/namadillo/src/App/Transfer/__tests__/ChainCard.test.tsx b/apps/namadillo/src/App/Transfer/__tests__/ChainCard.test.tsx
new file mode 100644
index 000000000..42c2da8fd
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/__tests__/ChainCard.test.tsx
@@ -0,0 +1,18 @@
+import { Chain } from "@chain-registry/types";
+import { render, screen } from "@testing-library/react";
+import { ChainCard } from "App/Transfer/ChainCard";
+import { randomChainMock } from "../__mocks__/chains";
+
+describe("Component: ChainCard", () => {
+ it("renders the chain's name", () => {
+ render();
+ expect(screen.getByText(randomChainMock.pretty_name!)).toBeInTheDocument();
+ });
+
+ it("renders the chain's logo", () => {
+ render();
+ const logo = screen.getByAltText(`${randomChainMock.pretty_name!} logo`);
+ expect(logo).toBeInTheDocument();
+ expect(logo).toHaveAttribute("src", randomChainMock.logo_URIs?.svg);
+ });
+});
diff --git a/apps/namadillo/src/App/Transfer/__tests__/SelectAssetModal.test.tsx b/apps/namadillo/src/App/Transfer/__tests__/SelectAssetModal.test.tsx
new file mode 100644
index 000000000..dc41cce87
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/__tests__/SelectAssetModal.test.tsx
@@ -0,0 +1,64 @@
+import { Asset } from "@chain-registry/types";
+import { fireEvent, render, screen, waitFor } from "@testing-library/react";
+import { SelectAssetModal } from "App/Transfer/SelectAssetModal";
+import { assetMockList } from "../__mocks__/assets";
+
+describe("SelectAssetModal", () => {
+ const onCloseMock = jest.fn();
+ const onSelectMock = jest.fn();
+
+ it("should render the modal title", () => {
+ render(
+
+ );
+ expect(screen.getByText("Select Asset")).toBeInTheDocument();
+ });
+
+ it("should render all assets", () => {
+ render(
+
+ );
+ expect(screen.getByText("Bitcoin")).toBeInTheDocument();
+ expect(screen.getByText("Ethereum")).toBeInTheDocument();
+ });
+
+ it("should filter assets based on search input", async () => {
+ render(
+
+ );
+ fireEvent.change(screen.getByPlaceholderText(/search/i, { exact: false }), {
+ target: { value: "Bit" },
+ });
+
+ // Event is debounced
+ waitFor(() => {
+ expect(screen.getByText("Bitcoin")).toBeInTheDocument();
+ expect(screen.queryByText("Ethereum")).not.toBeInTheDocument();
+ });
+ });
+
+ it("should call onSelect and onClose when an asset is selected", () => {
+ render(
+
+ );
+ fireEvent.click(screen.getByText("Bitcoin"));
+ expect(onSelectMock).toHaveBeenCalledWith(assetMockList[1]);
+ expect(onCloseMock).toHaveBeenCalled();
+ });
+});
diff --git a/apps/namadillo/src/App/Transfer/__tests__/SelectChainModal.test.tsx b/apps/namadillo/src/App/Transfer/__tests__/SelectChainModal.test.tsx
new file mode 100644
index 000000000..03d399692
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/__tests__/SelectChainModal.test.tsx
@@ -0,0 +1,60 @@
+import { Chains } from "@chain-registry/types";
+import { fireEvent, render, screen } from "@testing-library/react";
+import {
+ namadaChainMock,
+ randomChainMock,
+} from "App/Transfer/__mocks__/chains";
+import { SelectChainModal } from "App/Transfer/SelectChainModal";
+
+describe("Component: SelectChainModal", () => {
+ const mockChains = [randomChainMock, namadaChainMock];
+
+ it("should render component and list of chains correctly", () => {
+ render(
+
+ );
+ expect(screen.getByText("Namada")).toBeInTheDocument();
+ expect(screen.getByText("TestChain")).toBeInTheDocument();
+ //
+ // Check for modal title
+ expect(screen.getByText("Select Source Chain")).toBeInTheDocument();
+
+ // Check if all chains are rendered
+ mockChains.forEach((chain) => {
+ expect(screen.getByText(chain.pretty_name!)).toBeInTheDocument();
+ expect(
+ screen.getByAltText(`${chain.pretty_name} logo`)
+ ).toBeInTheDocument();
+ });
+ });
+
+ it("should select the correct chain on click", () => {
+ const handleSelect = jest.fn();
+ render(
+
+ );
+ fireEvent.click(screen.getByText(mockChains[0].pretty_name!));
+ expect(handleSelect).toHaveBeenCalledWith(mockChains[0]);
+ });
+
+ it("should display warning message if no chains were provided", () => {
+ const handleSelect = jest.fn();
+ render(
+
+ );
+ expect(screen.getByText(/no available chains/i, { exact: false }))
+ .toBeInTheDocument;
+ });
+});
diff --git a/apps/namadillo/src/App/Transfer/__tests__/SelectedAsset.test.tsx b/apps/namadillo/src/App/Transfer/__tests__/SelectedAsset.test.tsx
index 5ebe93cc1..c60b8f3a1 100644
--- a/apps/namadillo/src/App/Transfer/__tests__/SelectedAsset.test.tsx
+++ b/apps/namadillo/src/App/Transfer/__tests__/SelectedAsset.test.tsx
@@ -1,21 +1,11 @@
+import { Asset, Chain } from "@chain-registry/types";
import "@testing-library/jest-dom";
import { fireEvent, render, screen } from "@testing-library/react";
import { SelectedAsset } from "App/Transfer/SelectedAsset"; // Adjust the path accordingly
-import { Asset, Chain } from "types"; // Adjust the path accordingly
+import { assetMock } from "../__mocks__/assets";
+import { randomChainMock } from "../__mocks__/chains";
describe("SelectedAsset", () => {
- const mockChain: Chain = {
- chainId: "1",
- name: "Ethereum",
- iconUrl: "https://example.com/ethereum-icon.png",
- };
-
- const mockAsset: Partial = {
- name: "Ethereum",
- denomination: "ETH",
- iconUrl: "https://example.com/eth-icon.png",
- };
-
it("renders with no chain and disables the button", () => {
render();
const button = screen.getByRole("button");
@@ -24,7 +14,7 @@ describe("SelectedAsset", () => {
it("renders with no asset selected", () => {
const mockFn = jest.fn();
- render();
+ render();
const button = screen.getByRole("button");
expect(button).toBeEnabled();
@@ -40,8 +30,8 @@ describe("SelectedAsset", () => {
const handleClick = jest.fn();
render(
);
@@ -49,13 +39,11 @@ describe("SelectedAsset", () => {
const button = screen.getByRole("button");
expect(button).toBeEnabled();
- const assetDenomination = screen.getByText(mockAsset.denomination!);
+ const assetDenomination = screen.getByText(assetMock.symbol!);
expect(assetDenomination).toBeInTheDocument();
- const assetImage = screen.getByAltText(`${mockAsset.name} image`);
- expect(assetImage).toHaveStyle(
- `background-image: url(${mockAsset.iconUrl})`
- );
+ const assetImage = screen.getByAltText(`${assetMock.name} image`);
+ expect(assetImage).toHaveAttribute("src", assetMock.logo_URIs?.svg);
fireEvent.click(button);
expect(handleClick).toHaveBeenCalledTimes(1);
diff --git a/apps/namadillo/src/App/Transfer/__tests__/SelectedChain.test.tsx b/apps/namadillo/src/App/Transfer/__tests__/SelectedChain.test.tsx
index 9008be967..d00eae5cf 100644
--- a/apps/namadillo/src/App/Transfer/__tests__/SelectedChain.test.tsx
+++ b/apps/namadillo/src/App/Transfer/__tests__/SelectedChain.test.tsx
@@ -1,16 +1,11 @@
+import { Chain } from "@chain-registry/types";
import "@testing-library/jest-dom";
import { fireEvent, render, screen } from "@testing-library/react";
import { SelectedChain } from "App/Transfer/SelectedChain";
-import { Chain } from "types";
-import { providerConnectedMock } from "../__mocks__/providers";
+import { randomChainMock } from "../__mocks__/chains";
+import { walletMock } from "../__mocks__/providers";
describe("Component: SelectedChain", () => {
- const mockChain: Chain = {
- chainId: "chain-id",
- name: "Ethereum",
- iconUrl: "https://example.com/ethereum-icon.png",
- };
-
it("renders disabled with no provider selected", () => {
render();
const button = screen.getByRole("button");
@@ -18,14 +13,14 @@ describe("Component: SelectedChain", () => {
});
it("renders empty when chain is passed, but provider is disconnected", () => {
- render();
+ render();
const button = screen.getByRole("button");
expect(button).toBeDisabled();
expect(button.getAttribute("aria-description")).toMatch(/no chain/i);
});
it("renders correctly with no chain selected", () => {
- render();
+ render();
const button = screen.getByRole("button");
expect(button).toBeInTheDocument();
expect(button).toBeEnabled();
@@ -34,31 +29,28 @@ describe("Component: SelectedChain", () => {
it("renders correctly with chain selected", () => {
render(
-
+
);
const button = screen.getByRole("button");
expect(button).toBeInTheDocument();
- expect(button.getAttribute("aria-description")).toContain(mockChain.name);
+ expect(button.getAttribute("aria-description")).toContain(
+ randomChainMock.pretty_name
+ );
- const chainName = screen.getByText(mockChain.name);
+ const chainName = screen.getByText(randomChainMock.pretty_name!);
expect(chainName).toBeInTheDocument();
- const chainImage = screen.getByAltText(`${mockChain.name}`, {
+ const chainImage = screen.getByAltText(`${randomChainMock.pretty_name}`, {
exact: false,
});
expect(chainImage).toBeInTheDocument();
- expect(chainImage).toHaveAttribute(
- "style",
- `background-image: url(${mockChain.iconUrl});`
- );
+ expect(chainImage).toHaveAttribute("src", randomChainMock.logo_URIs?.svg);
});
it("calls onClick when the component is clicked", () => {
const handleClick = jest.fn();
- render(
-
- );
+ render();
const button = screen.getByRole("button");
fireEvent.click(button);
diff --git a/apps/namadillo/src/App/Transfer/__tests__/SelectedWallet.test.tsx b/apps/namadillo/src/App/Transfer/__tests__/SelectedWallet.test.tsx
new file mode 100644
index 000000000..33ed1f800
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/__tests__/SelectedWallet.test.tsx
@@ -0,0 +1,82 @@
+import { walletMock } from "../__mocks__/providers";
+const testWalletAddress = "0x1234567890abcdefghijkl";
+const mockIntegration = {
+ detect: jest.fn(),
+ connect: jest.fn(),
+ accounts: jest.fn().mockResolvedValue([{ address: testWalletAddress }]),
+};
+
+// Avoid hoisting
+(() => {
+ jest.mock("@namada/integrations", () => ({
+ integrations: {
+ [walletMock.id]: mockIntegration,
+ },
+ }));
+})();
+
+import { shortenAddress } from "@namada/utils";
+import {
+ act,
+ fireEvent,
+ render,
+ screen,
+ waitFor,
+} from "@testing-library/react";
+import { SelectedWallet } from "App/Transfer/SelectedWallet";
+
+describe("Component: SelectedWallet", () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it("should render the wallet icon", async () => {
+ await act(async () => {
+ render();
+ });
+ const walletIcon = screen.getByAltText(/logo/i, { exact: false });
+ expect(walletIcon).toBeInTheDocument();
+ expect(walletIcon).toHaveAttribute("src", walletMock.iconUrl);
+ });
+
+ it("should display the shortened wallet address after loading accounts", async () => {
+ render();
+
+ // Wait for the address to be loaded
+ await waitFor(() => {
+ expect(mockIntegration.connect).toHaveBeenCalled();
+ expect(mockIntegration.accounts).toHaveBeenCalled();
+ });
+
+ // Check if the address is correctly shortened
+ const shortenedAddress = shortenAddress(testWalletAddress, 8, 6);
+ expect(screen.getByText(shortenedAddress)).toBeInTheDocument();
+ });
+
+ it("should trigger the onClick function when clicked", async () => {
+ const onClickMock = jest.fn();
+ await act(async () => {
+ render();
+ });
+ const walletButton = screen.getByRole("button");
+ fireEvent.click(walletButton);
+ expect(onClickMock).toHaveBeenCalled();
+ });
+
+ it("should handle missing wallet address gracefully", async () => {
+ //Mock integration to return no accounts
+ mockIntegration.accounts.mockResolvedValue([]);
+
+ await act(async () => {
+ render();
+ });
+
+ // Wait for the component to try loading accounts
+ await waitFor(() => {
+ expect(mockIntegration.accounts).toHaveBeenCalled();
+ });
+
+ // Check that no address is displayed
+ expect(screen.queryByText(/0x/i)).toBeNull();
+ });
+});
diff --git a/apps/namadillo/src/App/Transfer/__tests__/TransferDestination.test.tsx b/apps/namadillo/src/App/Transfer/__tests__/TransferDestination.test.tsx
index fee5a7878..6cd71963d 100644
--- a/apps/namadillo/src/App/Transfer/__tests__/TransferDestination.test.tsx
+++ b/apps/namadillo/src/App/Transfer/__tests__/TransferDestination.test.tsx
@@ -1,3 +1,4 @@
+import { Chain } from "@chain-registry/types";
import { fireEvent, render, screen } from "@testing-library/react";
import {
namadaChainMock,
@@ -5,7 +6,7 @@ import {
} from "App/Transfer/__mocks__/chains";
import { TransferDestination } from "App/Transfer/TransferDestination";
import BigNumber from "bignumber.js";
-import { providerMock } from "../__mocks__/providers";
+import { walletMock } from "../__mocks__/providers";
describe("TransferDestination", () => {
it("should render the component with the default props", () => {
@@ -18,7 +19,7 @@ describe("TransferDestination", () => {
);
expect(screen.getByText("Shielded")).toBeInTheDocument();
@@ -34,7 +35,7 @@ describe("TransferDestination", () => {
render(
);
@@ -45,8 +46,8 @@ describe("TransferDestination", () => {
render(
);
expect(screen.getByText(/namada shielded/i)).toBeInTheDocument();
@@ -56,8 +57,8 @@ describe("TransferDestination", () => {
render(
);
expect(screen.getByText(/namada transparent/i)).toBeInTheDocument();
@@ -68,7 +69,7 @@ describe("TransferDestination", () => {
render(
);
diff --git a/apps/namadillo/src/App/Transfer/__tests__/TransferSource.test.tsx b/apps/namadillo/src/App/Transfer/__tests__/TransferSource.test.tsx
index b21c3c2c0..4c0413616 100644
--- a/apps/namadillo/src/App/Transfer/__tests__/TransferSource.test.tsx
+++ b/apps/namadillo/src/App/Transfer/__tests__/TransferSource.test.tsx
@@ -1,3 +1,4 @@
+import { Chain } from "@chain-registry/types";
import { fireEvent, render, screen } from "@testing-library/react";
import {
TransferSource,
@@ -5,7 +6,7 @@ import {
} from "App/Transfer/TransferSource";
import BigNumber from "bignumber.js";
import { namadaChainMock } from "../__mocks__/chains";
-import { providerConnectedMock } from "../__mocks__/providers";
+import { walletMock } from "../__mocks__/providers";
describe("Component: TransferSource", () => {
it("should render the component with the default props", () => {
@@ -45,7 +46,7 @@ describe("Component: TransferSource", () => {
const openChainSelectorMock = jest.fn();
setup({
openChainSelector: openChainSelectorMock,
- provider: providerConnectedMock,
+ wallet: walletMock,
});
const chain = getEmptyChain();
fireEvent.click(chain);
@@ -64,7 +65,10 @@ describe("Component: TransferSource", () => {
it("should call openAssetSelector when the SelectedAsset is clicked", () => {
const openAssetSelectorMock = jest.fn();
- setup({ openAssetSelector: openAssetSelectorMock, chain: namadaChainMock });
+ setup({
+ openAssetSelector: openAssetSelectorMock,
+ chain: namadaChainMock as Chain,
+ });
const assetControl = getEmptyAsset();
fireEvent.click(assetControl);
expect(openAssetSelectorMock).toHaveBeenCalled();
diff --git a/apps/namadillo/src/App/Transfer/__tests__/WalletCard.test.tsx b/apps/namadillo/src/App/Transfer/__tests__/WalletCard.test.tsx
new file mode 100644
index 000000000..9f0cc6cda
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/__tests__/WalletCard.test.tsx
@@ -0,0 +1,79 @@
+import { cleanup, fireEvent, render, screen } from "@testing-library/react";
+import { WalletCard } from "App/Transfer/WalletCard";
+import { walletMock } from "../__mocks__/providers";
+
+describe("Component: WalletCard", () => {
+ it("should render wallet name and icon", () => {
+ render(
+
+ );
+
+ expect(screen.getByText("Keplr")).toBeInTheDocument();
+ expect(screen.getByRole("img")).toHaveAttribute("src", walletMock.iconUrl);
+ });
+
+ const mockUserAgent = (userAgent: string): void => {
+ const fn = jest.spyOn(navigator, "userAgent", "get");
+ fn.mockReturnValue(userAgent);
+ };
+
+ it("should show 'Install' with correct link if wallet is not installed", () => {
+ const chromeUserAgent = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36`;
+ const firefoxUserAgent = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:130.0) Gecko/20100101 Firefox/130.0`;
+
+ // Render Chrome url
+ mockUserAgent(chromeUserAgent);
+ render(
+
+ );
+ const chromeLink = screen.getByText(/install/i);
+ expect(chromeLink).toBeInTheDocument();
+ expect(chromeLink).toHaveAttribute("href", walletMock.downloadUrl.chrome);
+
+ // Render firefox url
+ cleanup();
+ jest.clearAllMocks();
+ mockUserAgent(firefoxUserAgent);
+ render(
+
+ );
+ const firefoxLink = screen.getByText(/install/i);
+ expect(firefoxLink).toHaveAttribute("href", walletMock.downloadUrl.firefox);
+ });
+
+ it("should show 'Connect' button if wallet is installed but not connected", () => {
+ const onConnectMock = jest.fn();
+ render(
+
+ );
+
+ const connectButton = screen.getByText(/connect/i);
+ expect(connectButton).toBeInTheDocument();
+
+ fireEvent.click(connectButton);
+ expect(onConnectMock).toHaveBeenCalled();
+ });
+
+ it("should show 'Select' button if wallet is installed and connected", () => {
+ const onSelectMock = jest.fn();
+ render(
+
+ );
+
+ const selectButton = screen.getByText(/select/i);
+ expect(selectButton).toBeInTheDocument();
+
+ fireEvent.click(selectButton);
+ expect(onSelectMock).toHaveBeenCalled();
+ });
+});
diff --git a/apps/namadillo/src/App/Transfer/routes.ts b/apps/namadillo/src/App/Transfer/routes.ts
index 524459c38..df2ccb003 100644
--- a/apps/namadillo/src/App/Transfer/routes.ts
+++ b/apps/namadillo/src/App/Transfer/routes.ts
@@ -12,7 +12,7 @@ export const shield = (): RouteOutput => routeOutput("/shield");
export const shieldAll = (): RouteOutput => routeOutput(`/shield-all`);
-export const ibcTransfer = (): RouteOutput => routeOutput(`/ibc`);
+export const example = (): RouteOutput => routeOutput(`/example`);
export default {
index,
@@ -20,5 +20,5 @@ export default {
namTransfer,
shield,
shieldAll,
- ibcTransfer,
+ example,
};
diff --git a/apps/namadillo/src/atoms/exchangeRates.ts b/apps/namadillo/src/atoms/exchangeRates.ts
deleted file mode 100644
index a2d281fd5..000000000
--- a/apps/namadillo/src/atoms/exchangeRates.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { CurrencyType } from "@namada/utils";
-import { atom } from "jotai";
-import { selectedCurrencyAtom } from "./settings";
-
-type SupportedCurrencies = "nam";
-
-export type ExchangeRateTable = Record<
- SupportedCurrencies,
- Record
->;
-
-export const exchangeRateAtom = atom({
- nam: {
- usd: 0,
- eur: 0,
- jpy: 0,
- },
-});
-
-export const selectedCurrencyRateAtom = atom((get) => {
- const exchangeRates = get(exchangeRateAtom);
- const activeCurrency = get(selectedCurrencyAtom);
- return exchangeRates["nam"][activeCurrency];
-});
diff --git a/apps/namadillo/src/atoms/integrations/atoms.ts b/apps/namadillo/src/atoms/integrations/atoms.ts
new file mode 100644
index 000000000..32aa88691
--- /dev/null
+++ b/apps/namadillo/src/atoms/integrations/atoms.ts
@@ -0,0 +1,13 @@
+import { ExtensionKey } from "@namada/types";
+import { atomWithStorage } from "jotai/utils";
+
+// Currently we're just integrating with Keplr, but in the future we might use different wallets
+export const selectedIBCWallet = atomWithStorage(
+ "namadillo:ibc:wallet",
+ undefined
+);
+
+export const selectedIBCChainAtom = atomWithStorage(
+ "namadillo:ibc:chainId",
+ undefined
+);
diff --git a/apps/namadillo/src/atoms/integrations/index.ts b/apps/namadillo/src/atoms/integrations/index.ts
new file mode 100644
index 000000000..4e0d46d9a
--- /dev/null
+++ b/apps/namadillo/src/atoms/integrations/index.ts
@@ -0,0 +1 @@
+export * from "./atoms";
diff --git a/apps/namadillo/src/atoms/settings/atoms.ts b/apps/namadillo/src/atoms/settings/atoms.ts
index a4947f589..887db905f 100644
--- a/apps/namadillo/src/atoms/settings/atoms.ts
+++ b/apps/namadillo/src/atoms/settings/atoms.ts
@@ -1,4 +1,4 @@
-import { CurrencyType, isUrlValid, sanitizeUrl } from "@namada/utils";
+import { isUrlValid, sanitizeUrl } from "@namada/utils";
import { indexerRpcUrlAtom } from "atoms/chain";
import { Getter, Setter, atom, getDefaultStore } from "jotai";
import { atomWithMutation, atomWithQuery } from "jotai-tanstack-query";
@@ -84,11 +84,6 @@ const changeSettingsUrl =
}
};
-export const selectedCurrencyAtom = atom(
- (get) => get(settingsAtom).fiat,
- changeSettings("fiat")
-);
-
/**
* Returns RPC Url.
* Priority: user defined RPC Url > TOML config > indexer RPC url
diff --git a/apps/namadillo/src/hooks/useExtensionConnect.ts b/apps/namadillo/src/hooks/useExtensionConnect.ts
index 237eb7712..1e5b7d781 100644
--- a/apps/namadillo/src/hooks/useExtensionConnect.ts
+++ b/apps/namadillo/src/hooks/useExtensionConnect.ts
@@ -1,5 +1,5 @@
import { useIntegrationConnection } from "@namada/integrations";
-import { ChainKey } from "@namada/types";
+import { ExtensionKey } from "@namada/types";
import { ConnectStatus, namadaExtensionConnectionStatus } from "atoms/settings";
import { useAtom } from "jotai";
import { useEffect } from "react";
@@ -11,7 +11,7 @@ type UseConnectOutput = {
};
export const useExtensionConnect = (
- chainKey: ChainKey = "namada"
+ chainKey: ExtensionKey = "namada"
): UseConnectOutput => {
const [connectionStatus, setConnectionStatus] = useAtom(
namadaExtensionConnectionStatus
diff --git a/apps/namadillo/src/integrations/assets/keplr.svg b/apps/namadillo/src/integrations/assets/keplr.svg
new file mode 100644
index 000000000..e56c5257c
--- /dev/null
+++ b/apps/namadillo/src/integrations/assets/keplr.svg
@@ -0,0 +1,30 @@
+
diff --git a/apps/namadillo/src/integrations/assets/namada.svg b/apps/namadillo/src/integrations/assets/namada.svg
new file mode 100644
index 000000000..7f4e2a7fa
--- /dev/null
+++ b/apps/namadillo/src/integrations/assets/namada.svg
@@ -0,0 +1,4 @@
+
diff --git a/apps/namadillo/src/integrations/index.ts b/apps/namadillo/src/integrations/index.ts
new file mode 100644
index 000000000..a2604356d
--- /dev/null
+++ b/apps/namadillo/src/integrations/index.ts
@@ -0,0 +1,28 @@
+import { ExtensionKey } from "@namada/types";
+import { WalletProvider } from "types";
+import keplrSvg from "./assets/keplr.svg";
+import namadaSvg from "./assets/namada.svg";
+
+export const wallets: Partial> = {
+ keplr: {
+ id: "keplr",
+ name: "Keplr",
+ iconUrl: keplrSvg,
+ downloadUrl: {
+ chrome:
+ "https://chromewebstore.google.com/detail/keplr/dmkamcknogkgcdfhhbddcghachkejeap",
+ firefox: "https://addons.mozilla.org/en-US/firefox/addon/keplr/",
+ },
+ },
+
+ namada: {
+ id: "namada",
+ name: "Namada",
+ iconUrl: namadaSvg,
+ downloadUrl: {
+ chrome:
+ "https://chromewebstore.google.com/detail/namada-keychain/hnebcbhjpeejiclgbohcijljcnjdofek",
+ firefox: "",
+ },
+ },
+};
diff --git a/apps/namadillo/src/lib/query.ts b/apps/namadillo/src/lib/query.ts
index a2cb524a8..b7b26cd93 100644
--- a/apps/namadillo/src/lib/query.ts
+++ b/apps/namadillo/src/lib/query.ts
@@ -2,6 +2,7 @@ import { getIntegration } from "@namada/integrations";
import {
Account,
AccountType,
+ ExtensionKey,
Signer,
TxMsgValue,
TxProps,
@@ -138,11 +139,11 @@ export const buildTx = async (
* Asynchronously signs an encoded batch transaction using Namada extension.
*/
export const signTx = async (
- chain: ChainSettings,
+ wallet: ExtensionKey,
typedEncodedTx: EncodedTxData,
owner: string
): Promise => {
- const integration = getIntegration(chain.id);
+ const integration = getIntegration(wallet);
const signingClient = integration.signer() as Signer;
const store = getDefaultStore();
@@ -190,7 +191,7 @@ export const buildTxPair = async (
queryProps,
txFn
);
- const signedTxs = await signTx(chain, encodedTxData, owner);
+ const signedTxs = await signTx(chain.extensionId, encodedTxData, owner);
return {
signedTxs,
encodedTxData,
diff --git a/apps/namadillo/src/registry/index.ts b/apps/namadillo/src/registry/index.ts
index 86b83d5cf..d108a645f 100644
--- a/apps/namadillo/src/registry/index.ts
+++ b/apps/namadillo/src/registry/index.ts
@@ -1,5 +1,5 @@
import cosmoshub from "./cosmoshub.json";
-import namada from "./namada.json";
+import namada from "./namada-temp.json";
type MinimalDenom = string;
type ConfigName = string;
diff --git a/apps/namadillo/src/registry/namada-temp.json b/apps/namadillo/src/registry/namada-temp.json
new file mode 100644
index 000000000..7ec080145
--- /dev/null
+++ b/apps/namadillo/src/registry/namada-temp.json
@@ -0,0 +1,22 @@
+{
+ "chainName": "Namada",
+ "currencies": [
+ {
+ "coinDecimals": 6,
+ "coinDenom": "NAM",
+ "coinMinimalDenom": "namnam"
+ }
+ ],
+ "feeCurrencies": [
+ {
+ "coinDecimals": 6,
+ "coinDenom": "NAM",
+ "coinMinimalDenom": "namnam"
+ }
+ ],
+ "stakeCurrency": {
+ "coinDecimals": 6,
+ "coinDenom": "NAM",
+ "coinMinimalDenom": "namnam"
+ }
+}
diff --git a/apps/namadillo/src/registry/namada.json b/apps/namadillo/src/registry/namada.json
index 7ec080145..60f075611 100644
--- a/apps/namadillo/src/registry/namada.json
+++ b/apps/namadillo/src/registry/namada.json
@@ -1,22 +1,101 @@
{
- "chainName": "Namada",
- "currencies": [
- {
- "coinDecimals": 6,
- "coinDenom": "NAM",
- "coinMinimalDenom": "namnam"
+ "chain_name": "namada",
+ "status": "live",
+ "network_type": "mainnet",
+ "website": "https://namada.net/",
+ "update_link": "",
+ "pretty_name": "Namada",
+ "chain_type": "namada",
+ "chain_id": "namada-1",
+ "bech32_prefix": "nam",
+ "daemon_name": "nam",
+ "node_home": "",
+ "key_algos": ["secp256k1"],
+ "slip44": 118,
+ "fees": {
+ "fee_tokens": [
+ {
+ "denom": "namnam",
+ "fixed_min_gas_price": 0.0025,
+ "low_gas_price": 0.0025,
+ "average_gas_price": 0.025,
+ "high_gas_price": 0.04
+ }
+ ]
+ },
+ "staking": {
+ "staking_tokens": [
+ {
+ "denom": "nam"
+ }
+ ],
+ "lock_duration": {
+ "time": "1209600s"
}
- ],
- "feeCurrencies": [
+ },
+ "codebase": {
+ "git_repo": "https://github.com/anoma/namada",
+ "genesis": {
+ "name": "",
+ "genesis_url": ""
+ },
+ "recommended_version": "",
+ "compatible_versions": [""],
+ "cosmos_sdk_version": "",
+ "consensus": {
+ "type": "",
+ "version": ""
+ },
+ "cosmwasm_version": "",
+ "cosmwasm_enabled": true,
+ "ibc_go_version": "v7.4.0",
+ "ics_enabled": ["ics20-1"],
+ "binaries": {
+ "linux/amd64": "",
+ "linux/arm64": ""
+ },
+ "language": {
+ "type": "rust",
+ "version": "1.21.4"
+ },
+ "sdk": {
+ "type": "",
+ "repo": "",
+ "version": "",
+ "tag": ""
+ },
+ "ibc": {
+ "type": "",
+ "version": "",
+ "ics_enabled": [""]
+ },
+ "cosmwasm": {
+ "version": "",
+ "repo": "",
+ "tag": "",
+ "enabled": true
+ }
+ },
+ "images": [
{
- "coinDecimals": 6,
- "coinDenom": "NAM",
- "coinMinimalDenom": "namnam"
+ "image_sync": {
+ "chain_name": "namada",
+ "base_denom": "namnam"
+ },
+ "svg": "",
+ "png": "",
+ "theme": {
+ "primary_color_hex": "#ffff00"
+ }
}
],
- "stakeCurrency": {
- "coinDecimals": 6,
- "coinDenom": "NAM",
- "coinMinimalDenom": "namnam"
- }
+ "logo_URIs": {
+ "png": "",
+ "svg": ""
+ },
+ "description": "",
+ "peers": {
+ "seeds": []
+ },
+ "keywords": ["dex"]
}
diff --git a/apps/namadillo/src/types.d.ts b/apps/namadillo/src/types.d.ts
index 8f18d4a37..162fbbbae 100644
--- a/apps/namadillo/src/types.d.ts
+++ b/apps/namadillo/src/types.d.ts
@@ -167,10 +167,14 @@ export type ToastNotificationEntryFilter = (
notification: ToastNotification
) => boolean;
-export type Provider = {
+export type WalletProvider = {
+ id: ExtensionKey;
name: string;
iconUrl: string;
- connected: boolean;
+ downloadUrl: {
+ chrome: string;
+ firefox: string;
+ };
};
export type Chain = {
diff --git a/package.json b/package.json
index 2f73e615c..99fb8fdca 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
"docker-build-namadillo": "docker build -f docker/namadillo/Dockerfile ."
},
"devDependencies": {
+ "@chain-registry/types": "^0.45.80",
"@release-it/conventional-changelog": "^8.0.1",
"@typescript-eslint/eslint-plugin": "^8.4.0",
"@typescript-eslint/parser": "^8.4.0",
@@ -41,5 +42,8 @@
"vite-plugin-checker": "^0.6.4",
"wsrun": "^5.2.4"
},
- "packageManager": "yarn@4.0.2"
+ "packageManager": "yarn@4.0.2",
+ "dependencies": {
+ "chain-registry": "^1.63.100"
+ }
}
diff --git a/packages/components/src/Currency.tsx b/packages/components/src/Currency.tsx
index c0e6ee8dd..f7b1bea44 100644
--- a/packages/components/src/Currency.tsx
+++ b/packages/components/src/Currency.tsx
@@ -1,14 +1,20 @@
-import { KnownCurrencies } from "@namada/utils";
import BigNumber from "bignumber.js";
+export type CurrencyObject = {
+ symbol: string;
+ singular?: string;
+ plural?: string;
+ fraction?: string;
+};
+
export type CurrencyProps = {
amount: number | BigNumber;
hideBalances?: boolean;
- currency: keyof typeof KnownCurrencies;
+ currency: CurrencyObject;
separator?: "." | "," | "";
- spaceAroundSign?: boolean;
+ spaceAroundSymbol?: boolean;
currencyPosition?: "left" | "right";
- currencySignClassName?: string;
+ currencySymbolClassName?: string;
baseAmountClassName?: string;
fractionClassName?: string;
} & React.ComponentPropsWithoutRef<"span">;
@@ -20,23 +26,22 @@ export const Currency = ({
currencyPosition = "left",
separator = ".",
className = "",
- spaceAroundSign = false,
- currencySignClassName = "",
+ spaceAroundSymbol = false,
+ currencySymbolClassName = "",
baseAmountClassName = "",
fractionClassName = "",
...containerRest
}: CurrencyProps): JSX.Element => {
- const currencyObj = KnownCurrencies[currency];
const amountParts = BigNumber(amount).toFormat().split(".");
const baseAmount = hideBalances ? "✳✳✳✳" : amountParts[0] || "0";
const fraction =
amountParts.length > 1 && !hideBalances ? amountParts[1] : "";
const currencyHtml = (
-
- {spaceAroundSign && currencyPosition === "right" ? " " : null}
- {currencyObj.sign}
- {spaceAroundSign && currencyPosition === "left" ? " " : null}
+
+ {spaceAroundSymbol && currencyPosition === "right" ? " " : null}
+ {currency.symbol}
+ {spaceAroundSymbol && currencyPosition === "left" ? " " : null}
);
@@ -49,10 +54,11 @@ export const Currency = ({
);
const amountText =
- BigNumber(baseAmount).eq(1) ? currencyObj.singular : currencyObj.plural;
+ (BigNumber(baseAmount).eq(1) ? currency.singular : currency.plural) ||
+ currency.symbol;
const centsText =
- BigNumber(fraction).gt(0) ? ` and ${fraction} ${currencyObj.fraction}` : "";
+ BigNumber(fraction).gt(0) ? ` and ${fraction} ${currency.fraction}` : "";
const screenReaderText = `${baseAmount} ${amountText}${centsText}`;
diff --git a/packages/integrations/src/hooks/useIntegration.ts b/packages/integrations/src/hooks/useIntegration.ts
index d3c178e20..b23b56554 100644
--- a/packages/integrations/src/hooks/useIntegration.ts
+++ b/packages/integrations/src/hooks/useIntegration.ts
@@ -9,7 +9,7 @@ import {
import { chains } from "@namada/chains";
import { useUntil } from "@namada/hooks";
import { Keplr, Metamask, Namada } from "@namada/integrations";
-import { ChainKey, ExtensionKey } from "@namada/types";
+import { ExtensionKey } from "@namada/types";
type ExtensionConnection = (
onSuccess: () => T,
@@ -22,18 +22,14 @@ type IntegrationFromExtensionKey =
: K extends "metamask" ? Metamask
: never;
-type IntegrationFromChainKey = IntegrationFromExtensionKey<
- (typeof chains)[K]["extension"]["id"]
->;
-
type Integrations = {
- [K in ChainKey]: IntegrationFromChainKey;
+ [K in ExtensionKey]: IntegrationFromExtensionKey;
};
export const integrations: Integrations = {
namada: new Namada(chains.namada),
- cosmos: new Keplr(chains.cosmos),
- ethereum: new Metamask(chains.ethereum),
+ keplr: new Keplr(chains.cosmos),
+ metamask: new Metamask(chains.ethereum),
};
export const IntegrationsContext = createContext(integrations);
@@ -44,9 +40,9 @@ export const IntegrationsContext = createContext(integrations);
* @param {ChainIndex} chainKey - Index of chain integration
* @returns {InstanceType} Integration API
*/
-export const useIntegration = (
+export const useIntegration = (
chainKey: K
-): IntegrationFromChainKey => {
+): IntegrationFromExtensionKey => {
return useContext(IntegrationsContext)[chainKey];
};
@@ -55,18 +51,22 @@ export const useIntegration = (
*
* @template TSuccess - Success return type.
* @template TFail - Fail return type.
- * @param {ChainKey} chainKey - Index of a chain integration
+ * @param {ExtensionKey} extensionKey - Index of a wallet integration
* @returns {[InstanceType, boolean, ExtensionConnection]}
* Tuple of integration, connection status and connection function.
*/
-export const useIntegrationConnection = (
- chainKey: K
+export const useIntegrationConnection = <
+ TSuccess,
+ TFail,
+ K extends ExtensionKey,
+>(
+ extensionKey: K
): [
- IntegrationFromChainKey,
+ IntegrationFromExtensionKey,
boolean,
ExtensionConnection,
] => {
- const integration = useIntegration(chainKey);
+ const integration = useIntegration(extensionKey);
const [isConnectingToExtension, setIsConnectingToExtension] = useState(false);
const connect: ExtensionConnection = useCallback(
@@ -84,7 +84,7 @@ export const useIntegrationConnection = (
}
setIsConnectingToExtension(false);
},
- [chainKey]
+ [extensionKey]
);
return [integration, isConnectingToExtension, connect];
@@ -97,10 +97,9 @@ type AttachStatusMap = { [key in ExtensionKey]: AttachStatus };
* Hook used for returning attach status of extension
*/
export const useUntilIntegrationAttached = (
- chainId: ChainKey = "namada",
extensionId: ExtensionKey = "namada"
): AttachStatus => {
- const integration = useIntegration(chainId);
+ const integration = useIntegration(extensionId);
const [attachStatusMap, setAttachStatus] = useState({
namada: "pending",
keplr: "pending",
@@ -109,7 +108,7 @@ export const useUntilIntegrationAttached = (
useEffect(() => {
setAttachStatus((v) => ({ ...v, [extensionId]: "pending" }));
- }, [chainId]);
+ }, [extensionId]);
useUntil(
{
@@ -142,11 +141,11 @@ export const getIntegrations = (): Integrations => {
/**
* Returns integration by chainId. To be used outside react components.
*
- * @param {Chainkey} chainKey - Key of the chain
+ * @param {ExtensionKey} extensionKey - Key of the wallet
* @returns {InstanceType} Integration API
*/
-export const getIntegration = (
- chainKey: K
-): IntegrationFromChainKey => {
- return integrations[chainKey];
+export const getIntegration = (
+ extensionKey: K
+): IntegrationFromExtensionKey => {
+ return integrations[extensionKey];
};
diff --git a/packages/utils/src/currencies.ts b/packages/utils/src/currencies.ts
deleted file mode 100644
index f6466e6f5..000000000
--- a/packages/utils/src/currencies.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-export type CurrencyInfo = {
- sign: string;
- singular: string;
- plural: string;
- fraction: string;
- fiat: boolean;
-};
-
-export type CurrencyInfoListItem = {
- id: string;
-} & CurrencyInfo;
-
-export const KnownCurrencies: Record = {
- usd: {
- sign: "$",
- singular: "US Dollar",
- plural: "US Dollars",
- fraction: "cents",
- fiat: true,
- },
- eur: {
- sign: "€",
- singular: "Euro",
- plural: "Euros",
- fraction: "cents",
- fiat: true,
- },
- nam: {
- sign: "NAM",
- singular: "NAM",
- plural: "NAM",
- fraction: "cents",
- fiat: false,
- },
- jpy: {
- sign: "¥",
- singular: "Yen",
- plural: "Yen",
- fraction: "cents",
- fiat: true,
- },
-};
-
-export const CurrencyList: CurrencyInfoListItem[] = Object.keys(
- KnownCurrencies
-).map((currency) => ({
- id: currency,
- ...KnownCurrencies[currency],
-}));
-
-export const FiatCurrencyList = CurrencyList.filter(
- (currency) => currency.fiat
-);
-
-export type CurrencyType = keyof typeof KnownCurrencies;
diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts
index b3df192f2..b46343641 100644
--- a/packages/utils/src/index.ts
+++ b/packages/utils/src/index.ts
@@ -1,4 +1,3 @@
export * from "./async";
-export * from "./currencies";
export * from "./helpers";
export * from "./theme";
diff --git a/storybook/src/stories/Currency.stories.tsx b/storybook/src/stories/Currency.stories.tsx
index d52d00bff..0dd2e9801 100644
--- a/storybook/src/stories/Currency.stories.tsx
+++ b/storybook/src/stories/Currency.stories.tsx
@@ -7,16 +7,16 @@ export default {
argTypes: {},
} as Meta;
-type Story = StoryObj;
+type Story = StoryObj;
export const Default: Story = {
args: {
- currency: "nam",
+ currency: { symbol: "NAM" },
amount: 1000.56,
- spaceAroundSign: true,
+ spaceAroundSymbol: true,
separator: "",
currencyPosition: "right",
- currencySignClassName: "text-xl font-light",
+ currencySymbolClassName: "text-xl font-light",
baseAmountClassName: "text-3xl",
fractionClassName: "text-sm opacity-20",
className: "font-medium",
diff --git a/yarn.lock b/yarn.lock
index 129084ff6..e50dad60b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1588,6 +1588,36 @@ __metadata:
languageName: node
linkType: hard
+"@chain-registry/client@npm:^1.48.80":
+ version: 1.48.80
+ resolution: "@chain-registry/client@npm:1.48.80"
+ dependencies:
+ "@chain-registry/types": "npm:^0.45.80"
+ "@chain-registry/utils": "npm:^1.46.80"
+ bfs-path: "npm:^1.0.2"
+ cross-fetch: "npm:^3.1.5"
+ checksum: 63a8b398add4547145740b2ab40977a6305cd8b5c7f9c6339c46a36a28a52c476eb3a263328611db0fc986bcd24ea0e8a9fe54688acf38c8086988ce4b370ae0
+ languageName: node
+ linkType: hard
+
+"@chain-registry/types@npm:^0.45.80":
+ version: 0.45.80
+ resolution: "@chain-registry/types@npm:0.45.80"
+ checksum: 5ec1901865f00beda568683ca715984e0052f4c9d387a527df00538417d0bc9acaddcf8ce8d3faf8048edae66777b719b6b16d91eae030793b085c66eee71e7b
+ languageName: node
+ linkType: hard
+
+"@chain-registry/utils@npm:^1.46.80":
+ version: 1.46.80
+ resolution: "@chain-registry/utils@npm:1.46.80"
+ dependencies:
+ "@chain-registry/types": "npm:^0.45.80"
+ bignumber.js: "npm:9.1.2"
+ sha.js: "npm:^2.4.11"
+ checksum: 096b47bc36b275ee100bb9a1dd9ce3fca6e1079230ccccbf663159376d39e743278f56bf95a2e9037a289cb6f19d1f48434f58d55c45e4ea09610d065cdf387b
+ languageName: node
+ linkType: hard
+
"@confio/ics23@npm:^0.6.8":
version: 0.6.8
resolution: "@confio/ics23@npm:0.6.8"
@@ -3625,6 +3655,7 @@ __metadata:
resolution: "@namada/namadillo@workspace:apps/namadillo"
dependencies:
"@anomaorg/namada-indexer-client": "npm:0.0.23"
+ "@chain-registry/client": "npm:^1.48.80"
"@cosmjs/encoding": "npm:^0.32.3"
"@eslint/js": "npm:^9.9.1"
"@playwright/test": "npm:^1.24.1"
@@ -3652,6 +3683,7 @@ __metadata:
bignumber.js: "npm:^9.1.1"
clsx: "npm:^2.1.1"
crypto-browserify: "npm:^3.12.0"
+ currency-list: "npm:^1.0.8"
dotenv: "npm:^16.0.3"
eslint: "npm:^8.57.0"
eslint-config-prettier: "npm:^9.1.0"
@@ -7025,6 +7057,13 @@ __metadata:
languageName: node
linkType: hard
+"bfs-path@npm:^1.0.2":
+ version: 1.0.2
+ resolution: "bfs-path@npm:1.0.2"
+ checksum: 776cd5cf823d0767bab64d9c029bcf3336a5ee3a3e15f8ef9186772885fa2a3dd2bf4e3a5a5e7a96d02805a85d983a51d0aff76712a5b5c0b331db37578d0b79
+ languageName: node
+ linkType: hard
+
"big.js@npm:^5.2.2":
version: 5.2.2
resolution: "big.js@npm:5.2.2"
@@ -7032,7 +7071,7 @@ __metadata:
languageName: node
linkType: hard
-"bignumber.js@npm:^9.1.1":
+"bignumber.js@npm:9.1.2, bignumber.js@npm:^9.1.1":
version: 9.1.2
resolution: "bignumber.js@npm:9.1.2"
checksum: e17786545433f3110b868725c449fa9625366a6e675cd70eb39b60938d6adbd0158cb4b3ad4f306ce817165d37e63f4aa3098ba4110db1d9a3b9f66abfbaf10d
@@ -7561,6 +7600,15 @@ __metadata:
languageName: node
linkType: hard
+"chain-registry@npm:^1.63.100":
+ version: 1.63.100
+ resolution: "chain-registry@npm:1.63.100"
+ dependencies:
+ "@chain-registry/types": "npm:^0.45.80"
+ checksum: 9ba5f5bdfe36891bcf94ce3e95506fb36dc953dd05e8c4664a527cad08b294e4beaabd68f409e6384cd21e44f88991475b75bc0461ba29da9fdc503d87dd05c0
+ languageName: node
+ linkType: hard
+
"chalk@npm:4.1.2, chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2":
version: 4.1.2
resolution: "chalk@npm:4.1.2"
@@ -8625,7 +8673,7 @@ __metadata:
languageName: node
linkType: hard
-"cross-fetch@npm:^3.0.4":
+"cross-fetch@npm:^3.0.4, cross-fetch@npm:^3.1.5":
version: 3.1.8
resolution: "cross-fetch@npm:3.1.8"
dependencies:
@@ -8913,6 +8961,13 @@ __metadata:
languageName: node
linkType: hard
+"currency-list@npm:^1.0.8":
+ version: 1.0.8
+ resolution: "currency-list@npm:1.0.8"
+ checksum: 8563987ce681affb9b3d48a54df0565efa14cba75a1a8460d7ee6e0644c2161649438bf32791f33fd68f7c0d8b9fa7c9456a855837d7cda95222d2e506e63031
+ languageName: node
+ linkType: hard
+
"curve25519-js@npm:0.0.4":
version: 0.0.4
resolution: "curve25519-js@npm:0.0.4"
@@ -15736,9 +15791,11 @@ __metadata:
version: 0.0.0-use.local
resolution: "namada@workspace:."
dependencies:
+ "@chain-registry/types": "npm:^0.45.80"
"@release-it/conventional-changelog": "npm:^8.0.1"
"@typescript-eslint/eslint-plugin": "npm:^8.4.0"
"@typescript-eslint/parser": "npm:^8.4.0"
+ chain-registry: "npm:^1.63.100"
eslint: "npm:^8.57.0"
git-commit-msg-linter: "npm:^5.0.6"
husky: "npm:^8.0.3"