diff --git a/apps/faucet/webpack.config.js b/apps/faucet/webpack.config.js
index af72cdeca..e9e6debc8 100644
--- a/apps/faucet/webpack.config.js
+++ b/apps/faucet/webpack.config.js
@@ -10,8 +10,6 @@ require("dotenv").config({ path: resolve(__dirname, ".env") });
const { NODE_ENV } = process.env;
-const ASSET_PATH = "/";
-
const createStyledComponentsTransformer =
require("typescript-plugin-styled-components").default;
@@ -62,7 +60,7 @@ module.exports = {
faucet: "./src",
},
output: {
- publicPath: ASSET_PATH,
+ publicPath: "/",
path: resolve(__dirname, `./build/`),
filename: "[name].bundle.js",
},
diff --git a/apps/namadillo/src/App/AccountOverview/AccountOverview.tsx b/apps/namadillo/src/App/AccountOverview/AccountOverview.tsx
index 1214e7f84..168c42c3f 100644
--- a/apps/namadillo/src/App/AccountOverview/AccountOverview.tsx
+++ b/apps/namadillo/src/App/AccountOverview/AccountOverview.tsx
@@ -63,7 +63,9 @@ export const AccountOverview = (): JSX.Element => {
{showSidebar &&
(maspEnabled ?
-
+
: )}
diff --git a/apps/namadillo/src/App/Assets/ShieldedParty.svg b/apps/namadillo/src/App/Assets/ShieldedParty.svg
new file mode 100644
index 000000000..cec1470c6
--- /dev/null
+++ b/apps/namadillo/src/App/Assets/ShieldedParty.svg
@@ -0,0 +1,290 @@
+
+
diff --git a/apps/namadillo/src/App/Common/SelectModal.tsx b/apps/namadillo/src/App/Common/SelectModal.tsx
new file mode 100644
index 000000000..6e92dcc7f
--- /dev/null
+++ b/apps/namadillo/src/App/Common/SelectModal.tsx
@@ -0,0 +1,40 @@
+import { Modal } from "@namada/components";
+import clsx from "clsx";
+import React from "react";
+import { IoClose } from "react-icons/io5";
+import { ModalTransition } from "./ModalTransition";
+
+type SelectModalProps = {
+ children: React.ReactNode;
+ title: React.ReactNode;
+ onClose: () => void;
+};
+
+export const SelectModal = ({
+ children,
+ title,
+ onClose,
+}: SelectModalProps): JSX.Element => {
+ return (
+
+
+
+
+ {children}
+
+
+
+ );
+};
diff --git a/apps/namadillo/src/App/Common/TabSelector.tsx b/apps/namadillo/src/App/Common/TabSelector.tsx
new file mode 100644
index 000000000..29e0eea6b
--- /dev/null
+++ b/apps/namadillo/src/App/Common/TabSelector.tsx
@@ -0,0 +1,42 @@
+import clsx from "clsx";
+import { twMerge } from "tailwind-merge";
+type TabSelectorItem = {
+ text: React.ReactNode;
+ id: string;
+ className?: string;
+};
+
+type TabSelectorProps = {
+ items: TabSelectorItem[];
+ active: string;
+ onChange: (item: TabSelectorItem) => void;
+};
+
+export const TabSelector = ({
+ items,
+ active,
+ onChange,
+}: TabSelectorProps): JSX.Element => {
+ return (
+
+ );
+};
diff --git a/apps/namadillo/src/App/Governance/SubmitVote.tsx b/apps/namadillo/src/App/Governance/SubmitVote.tsx
index f373c5f67..09e3dd8b7 100644
--- a/apps/namadillo/src/App/Governance/SubmitVote.tsx
+++ b/apps/namadillo/src/App/Governance/SubmitVote.tsx
@@ -57,6 +57,7 @@ export const WithProposalId: React.FC<{ proposalId: bigint }> = ({
data: voteTxData,
isError,
error: voteTxError,
+ isPending: isPerformingTx,
} = useAtomValue(createVoteTxAtom);
const dispatchNotification = useSetAtom(dispatchToastNotificationAtom);
@@ -191,10 +192,12 @@ export const WithProposalId: React.FC<{ proposalId: bigint }> = ({
- Confirm
+ {isPerformingTx ? "Processing..." : "Confirm"}
)}
diff --git a/apps/namadillo/src/App/Sidebars/ShieldAllBanner.tsx b/apps/namadillo/src/App/Sidebars/ShieldAllBanner.tsx
index c26d0e96b..3d228cbd8 100644
--- a/apps/namadillo/src/App/Sidebars/ShieldAllBanner.tsx
+++ b/apps/namadillo/src/App/Sidebars/ShieldAllBanner.tsx
@@ -1,8 +1,13 @@
import { ActionButton } from "@namada/components";
+import svgImg from "App/Assets/ShieldedParty.svg";
import TransferRoutes from "App/Transfer/routes";
+import { useState } from "react";
+import { Link } from "react-router-dom";
import { twMerge } from "tailwind-merge";
export const ShieldAllBanner = (): JSX.Element => {
+ const [isAnimating, setIsAnimating] = useState(false);
+
return (
{
"p-3"
)}
>
-
img
-
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/:target
+ // https://gist.github.com/LeaVerou/5198257
+ src={`${svgImg}${isAnimating ? "#hover " : ""}`}
+ />
+ setIsAnimating(true)}
+ onMouseLeave={() => setIsAnimating(false)}
>
- Shield All Assets
-
+
+ Shield All Assets
+
+
);
};
diff --git a/apps/namadillo/src/App/Transfer/AvailableAmountFooter.tsx b/apps/namadillo/src/App/Transfer/AvailableAmountFooter.tsx
new file mode 100644
index 000000000..e4a50c47a
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/AvailableAmountFooter.tsx
@@ -0,0 +1,54 @@
+import { ActionButton, Currency } from "@namada/components";
+import { KnownCurrencies } from "@namada/utils";
+import BigNumber from "bignumber.js";
+import clsx from "clsx";
+
+type AvailableAmountFooterProps = {
+ availableAmount?: BigNumber;
+ currency?: keyof typeof KnownCurrencies;
+ onClickMax?: () => void;
+};
+
+export const AvailableAmountFooter = ({
+ availableAmount,
+ currency,
+ onClickMax,
+}: AvailableAmountFooterProps): JSX.Element => {
+ if (!currency || availableAmount === undefined) {
+ return <>>;
+ }
+
+ return (
+
+
+ Available:
+
+
+
+ {onClickMax && (
+
+ Max
+
+ )}
+
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/ChainCard.tsx b/apps/namadillo/src/App/Transfer/ChainCard.tsx
new file mode 100644
index 000000000..e69de29bb
diff --git a/apps/namadillo/src/App/Transfer/ConnectProviderButton.tsx b/apps/namadillo/src/App/Transfer/ConnectProviderButton.tsx
new file mode 100644
index 000000000..b5935dd22
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/ConnectProviderButton.tsx
@@ -0,0 +1,21 @@
+import { ActionButton } from "@namada/components";
+
+type ConnectProviderButtonProps = {
+ onClick?: () => void;
+};
+
+export const ConnectProviderButton = ({
+ onClick,
+}: ConnectProviderButtonProps): JSX.Element => {
+ return (
+
+ Connect Wallet
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/CustomAddressForm.tsx b/apps/namadillo/src/App/Transfer/CustomAddressForm.tsx
new file mode 100644
index 000000000..981386511
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/CustomAddressForm.tsx
@@ -0,0 +1,35 @@
+import { Input, Stack } from "@namada/components";
+
+type CustomAddressFormProps = {
+ onChangeAddress?: (address: string | undefined) => void;
+ customAddress?: string;
+ memo?: string;
+ onChangeMemo?: (address: string) => void;
+};
+
+export const CustomAddressForm = ({
+ customAddress,
+ onChangeAddress,
+ memo,
+ onChangeMemo,
+}: CustomAddressFormProps): JSX.Element => {
+ return (
+
+ {onChangeAddress && (
+ onChangeAddress(e.target.value)}
+ />
+ )}
+ {onChangeMemo && (
+ onChangeMemo(e.target.value)}
+ placeholder="Required for centralized exchanges"
+ />
+ )}
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/EmptyResourceIcon.tsx b/apps/namadillo/src/App/Transfer/EmptyResourceIcon.tsx
new file mode 100644
index 000000000..555021e8c
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/EmptyResourceIcon.tsx
@@ -0,0 +1,20 @@
+import clsx from "clsx";
+import { twMerge } from "tailwind-merge";
+type EmptyResourceProps = { className?: string };
+
+export const EmptyResourceIcon = ({
+ className = "",
+}: EmptyResourceProps): JSX.Element => {
+ return (
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/IBCFromNamadaModule.tsx b/apps/namadillo/src/App/Transfer/IBCFromNamadaModule.tsx
new file mode 100644
index 000000000..088f5a72c
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/IBCFromNamadaModule.tsx
@@ -0,0 +1,9 @@
+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
new file mode 100644
index 000000000..3a551ec52
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/IBCTransfers.tsx
@@ -0,0 +1,12 @@
+import { Panel } from "@namada/components";
+import { IBCFromNamadaModule } from "./IBCFromNamadaModule";
+
+export const IBCTransfers = (): JSX.Element => {
+ return (
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/SelectAssetsModal.tsx b/apps/namadillo/src/App/Transfer/SelectAssetsModal.tsx
new file mode 100644
index 000000000..8faf84a6c
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/SelectAssetsModal.tsx
@@ -0,0 +1,15 @@
+import { SelectModal } from "App/Common/SelectModal";
+
+type SelectItemsModalProps = {
+ onClose: () => void;
+};
+
+export const SelectAssetsModal = ({
+ onClose,
+}: SelectItemsModalProps): JSX.Element => {
+ return (
+
+ <>>
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/SelectChainModal.tsx b/apps/namadillo/src/App/Transfer/SelectChainModal.tsx
new file mode 100644
index 000000000..61fed101b
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/SelectChainModal.tsx
@@ -0,0 +1,34 @@
+import { Stack } from "@namada/components";
+import { SelectModal } from "App/Common/SelectModal";
+import clsx from "clsx";
+import { Chain } from "types";
+
+type SelectChainModalProps = {
+ onClose: () => void;
+ chains: Chain[];
+};
+
+export const SelectChainModal = ({
+ onClose,
+ chains,
+}: SelectChainModalProps): JSX.Element => {
+ return (
+
+
+ {chains.map((chain) => (
+
+
+
+ ))}
+
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/SelectProviderModal.tsx b/apps/namadillo/src/App/Transfer/SelectProviderModal.tsx
new file mode 100644
index 000000000..88fcb4eb7
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/SelectProviderModal.tsx
@@ -0,0 +1,43 @@
+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/SelectedAsset.tsx b/apps/namadillo/src/App/Transfer/SelectedAsset.tsx
new file mode 100644
index 000000000..4f86e205b
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/SelectedAsset.tsx
@@ -0,0 +1,51 @@
+import clsx from "clsx";
+import { GoChevronDown } from "react-icons/go";
+import { Asset, Chain } from "types";
+import { EmptyResourceIcon } from "./EmptyResourceIcon";
+
+type SelectedAssetProps = {
+ chain?: Chain;
+ asset?: Asset;
+ onClick?: () => void;
+};
+
+export const SelectedAsset = ({
+ chain,
+ asset,
+ onClick,
+}: SelectedAssetProps): JSX.Element => {
+ const selectorClassList = clsx(
+ `flex items-center gap-2.5 text-lg text-white font-light cursor-pointer uppercase`
+ );
+
+ return (
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/SelectedChain.tsx b/apps/namadillo/src/App/Transfer/SelectedChain.tsx
new file mode 100644
index 000000000..c2f923d0e
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/SelectedChain.tsx
@@ -0,0 +1,53 @@
+import clsx from "clsx";
+import { GoChevronDown } from "react-icons/go";
+import { Chain, Provider } from "types";
+import { EmptyResourceIcon } from "./EmptyResourceIcon";
+
+type SelectedChainProps = {
+ chain?: Chain;
+ provider?: Provider;
+ onClick?: () => void;
+};
+
+export const SelectedChain = ({
+ chain,
+ provider,
+ onClick,
+}: SelectedChainProps): JSX.Element => {
+ const selectorClassList = clsx(
+ `flex items-center gap-2.5 text-white font-light cursor-pointer`
+ );
+
+ return (
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/Transfer.tsx b/apps/namadillo/src/App/Transfer/Transfer.tsx
index c1fcd2a81..26bc34d25 100644
--- a/apps/namadillo/src/App/Transfer/Transfer.tsx
+++ b/apps/namadillo/src/App/Transfer/Transfer.tsx
@@ -1,4 +1,5 @@
import { Route, Routes } from "react-router-dom";
+import { IBCTransfers } from "./IBCTransfers";
import { NamTransfer } from "./NamTransfer";
import { Shield } from "./Shield";
import { ShieldAll } from "./ShieldAll";
@@ -16,6 +17,10 @@ export const Transfer: React.FC = () => (
path={TransferRoutes.shieldAll().toString()}
element={}
/>
+ }
+ />
);
diff --git a/apps/namadillo/src/App/Transfer/TransferDestination.tsx b/apps/namadillo/src/App/Transfer/TransferDestination.tsx
new file mode 100644
index 000000000..a9f454622
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/TransferDestination.tsx
@@ -0,0 +1,103 @@
+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 namadaShieldedSvg from "./assets/namada-shielded.svg";
+import namadaTransparentSvg from "./assets/namada-transparent.svg";
+import { CustomAddressForm } from "./CustomAddressForm";
+import { SelectedChain } from "./SelectedChain";
+
+type TransferDestinationProps = {
+ isShielded?: boolean;
+ onChangeShielded?: (isShielded: boolean) => void;
+ chain?: Chain;
+ provider?: Provider;
+ className?: string;
+ transactionFee?: BigNumber;
+ customAddressActive?: boolean;
+ onToggleCustomAddress?: (isActive: boolean) => void;
+ onChangeAddress?: (address: string | undefined) => void;
+ address?: string;
+ memo?: string;
+ onChangeMemo?: (address: string) => void;
+};
+
+const parseChainInfo = (
+ chain?: Chain,
+ isShielded?: boolean
+): Chain | undefined => {
+ if (chain?.name !== "Namada") {
+ return chain;
+ }
+ return {
+ ...chain,
+ name: isShielded ? "Namada Shielded" : "Namada Transparent",
+ iconUrl: isShielded ? namadaShieldedSvg : namadaTransparentSvg,
+ };
+};
+
+export const TransferDestination = ({
+ chain,
+ provider,
+ isShielded,
+ onChangeShielded,
+ transactionFee,
+ customAddressActive,
+ onToggleCustomAddress,
+ address,
+ onChangeAddress,
+ memo,
+ onChangeMemo,
+}: TransferDestinationProps): JSX.Element => {
+ return (
+
+ {onChangeShielded && chain?.name === "Namada" && (
+ onChangeShielded(!isShielded)}
+ />
+ )}
+
+ {onToggleCustomAddress && (
+ onToggleCustomAddress(!customAddressActive)}
+ items={[
+ { id: "my-address", text: "My Address", className: "text-white" },
+ { id: "custom", text: "Custom Address", className: "text-white" },
+ ]}
+ />
+ )}
+
+
+
+ {customAddressActive && (
+
+ )}
+
+ {transactionFee && (
+
+ )}
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/TransferModule.tsx b/apps/namadillo/src/App/Transfer/TransferModule.tsx
new file mode 100644
index 000000000..04d198e97
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/TransferModule.tsx
@@ -0,0 +1,85 @@
+import { Stack } from "@namada/components";
+import BigNumber from "bignumber.js";
+import { useState } from "react";
+import { Asset, Chain } from "types";
+import { SelectProviderModal } from "./SelectProviderModal";
+import { TransferDestination } from "./TransferDestination";
+import { TransferSource } from "./TransferSource";
+
+type TransferModuleProps = {
+ isConnected: boolean;
+ sourceChain?: Chain;
+ onChangeSourceChain?: () => void;
+ destinationChain?: Chain;
+ onChangeDestinationChain?: (chain: Chain) => void;
+ selectedAsset?: Asset;
+ onChangeSelectedAsset?: (asset: Asset | undefined) => void;
+ amount?: BigNumber;
+ onChangeAmount?: (amount: BigNumber) => void;
+ isShielded?: boolean;
+ onChangeShielded?: (isShielded: boolean) => void;
+ enableCustomAddress?: boolean;
+ onSubmitTransfer: () => void;
+};
+
+export const TransferModule = ({
+ isConnected,
+ selectedAsset,
+ sourceChain,
+ destinationChain,
+ isShielded,
+ onChangeShielded,
+ enableCustomAddress,
+}: TransferModuleProps): JSX.Element => {
+ const [providerSelectorModalOpen, setProviderSelectorModalOpen] =
+ useState(false);
+ const [chainSelectorModalOpen, setChainSelectorModalOpen] = 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));
+
+ return (
+ <>
+
+
+ setProviderSelectorModalOpen(true)}
+ openChainSelector={() => setChainSelectorModalOpen(true)}
+ openAssetSelector={() => setAssetSelectorModalOpen(true)}
+ amount={amount}
+ onChangeAmount={(e) =>
+ setAmount(e.target.value || new BigNumber(0))
+ }
+ />
+
+ {chainSelectorModalOpen && }
+ {assetSelectorModalOpen && }
+
+
+ {providerSelectorModalOpen && (
+ setProviderSelectorModalOpen(false)}
+ onConnect={() => {}}
+ />
+ )}
+ >
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/TransferSource.tsx b/apps/namadillo/src/App/Transfer/TransferSource.tsx
new file mode 100644
index 000000000..3e52ca813
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/TransferSource.tsx
@@ -0,0 +1,64 @@
+import { AmountInput, ChangeAmountEvent } from "@namada/components";
+import BigNumber from "bignumber.js";
+import clsx from "clsx";
+import { Asset, Chain, Provider } from "types";
+import { AvailableAmountFooter } from "./AvailableAmountFooter";
+import { ConnectProviderButton } from "./ConnectProviderButton";
+import { SelectedAsset } from "./SelectedAsset";
+import { SelectedChain } from "./SelectedChain";
+
+export type TransferSourceProps = {
+ isConnected: boolean;
+ provider?: Provider;
+ asset?: Asset;
+ chain?: Chain;
+ openChainSelector?: () => void;
+ openAssetSelector?: () => void;
+ openProviderSelector?: () => void;
+ amount?: BigNumber;
+ onChangeAmount?: ChangeAmountEvent;
+};
+
+export const TransferSource = ({
+ chain,
+ asset,
+ provider,
+ openProviderSelector,
+ openChainSelector,
+ openAssetSelector,
+ amount,
+ onChangeAmount,
+}: TransferSourceProps): JSX.Element => {
+ return (
+
+ );
+};
diff --git a/apps/namadillo/src/App/Transfer/__mocks__/chains.ts b/apps/namadillo/src/App/Transfer/__mocks__/chains.ts
new file mode 100644
index 000000000..da2ad5502
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/__mocks__/chains.ts
@@ -0,0 +1,13 @@
+import { Chain } from "types";
+
+export const namadaChainMock: Chain = {
+ chainId: "test",
+ name: "Namada",
+ iconUrl: "namada-icon",
+};
+
+export const randomChainMock: Chain = {
+ chainId: "test",
+ name: "TestChain",
+ iconUrl: "testchain-icon",
+};
diff --git a/apps/namadillo/src/App/Transfer/__mocks__/providers.ts b/apps/namadillo/src/App/Transfer/__mocks__/providers.ts
new file mode 100644
index 000000000..af2cd0157
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/__mocks__/providers.ts
@@ -0,0 +1,12 @@
+import { Provider } from "types";
+
+export const providerMock: Provider = {
+ name: "Keplr",
+ iconUrl: "test.svg",
+ connected: false,
+};
+
+export const providerConnectedMock: Provider = {
+ ...providerMock,
+ connected: true,
+};
diff --git a/apps/namadillo/src/App/Transfer/__tests__/AvailableAmountFooter.test.tsx b/apps/namadillo/src/App/Transfer/__tests__/AvailableAmountFooter.test.tsx
new file mode 100644
index 000000000..088ff37b3
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/__tests__/AvailableAmountFooter.test.tsx
@@ -0,0 +1,52 @@
+import { fireEvent, render, screen } from "@testing-library/react";
+import BigNumber from "bignumber.js";
+import { AvailableAmountFooter } from "../AvailableAmountFooter";
+
+describe("Component: AvailableAmountFooter", () => {
+ it("should render an empty tag when no available amount is provided", () => {
+ const { container } = render();
+ expect(container).toBeEmptyDOMElement();
+ });
+
+ it("should render with correct information and behaviour enabled", () => {
+ 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(button).toBeEnabled();
+ fireEvent.click(button);
+ expect(callback).toHaveBeenCalledTimes(1);
+ });
+
+ it("should not display MAX button when no callback was provided", () => {
+ render(
+
+ );
+ expect(screen.queryByRole("button")).not.toBeInTheDocument();
+ });
+
+ it("should display disabled button when the amount is zero", () => {
+ const callback = jest.fn();
+ render(
+
+ );
+ const button = screen.getByRole("button");
+ expect(button).toBeDisabled();
+ fireEvent.click(button);
+ expect(callback).not.toHaveBeenCalled();
+ });
+});
diff --git a/apps/namadillo/src/App/Transfer/__tests__/SelectedAsset.test.tsx b/apps/namadillo/src/App/Transfer/__tests__/SelectedAsset.test.tsx
new file mode 100644
index 000000000..5ebe93cc1
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/__tests__/SelectedAsset.test.tsx
@@ -0,0 +1,71 @@
+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
+
+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");
+ expect(button).toBeDisabled();
+ });
+
+ it("renders with no asset selected", () => {
+ const mockFn = jest.fn();
+ render();
+
+ const button = screen.getByRole("button");
+ expect(button).toBeEnabled();
+
+ const assetLabel = screen.getByText(/asset/i);
+ expect(assetLabel).toBeInTheDocument();
+
+ fireEvent.click(button);
+ expect(mockFn).toHaveBeenCalledTimes(1);
+ });
+
+ it("renders with asset selected", () => {
+ const handleClick = jest.fn();
+ render(
+
+ );
+
+ const button = screen.getByRole("button");
+ expect(button).toBeEnabled();
+
+ const assetDenomination = screen.getByText(mockAsset.denomination!);
+ expect(assetDenomination).toBeInTheDocument();
+
+ const assetImage = screen.getByAltText(`${mockAsset.name} image`);
+ expect(assetImage).toHaveStyle(
+ `background-image: url(${mockAsset.iconUrl})`
+ );
+
+ fireEvent.click(button);
+ expect(handleClick).toHaveBeenCalledTimes(1);
+ });
+
+ it("does not call onClick when the button is disabled", () => {
+ const handleClick = jest.fn();
+ render();
+ const button = screen.getByRole("button");
+ fireEvent.click(button);
+ expect(handleClick).not.toHaveBeenCalled();
+ });
+});
diff --git a/apps/namadillo/src/App/Transfer/__tests__/SelectedChain.test.tsx b/apps/namadillo/src/App/Transfer/__tests__/SelectedChain.test.tsx
new file mode 100644
index 000000000..9008be967
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/__tests__/SelectedChain.test.tsx
@@ -0,0 +1,67 @@
+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";
+
+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");
+ expect(button).toBeDisabled();
+ });
+
+ it("renders empty when chain is passed, but provider is disconnected", () => {
+ 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();
+ const button = screen.getByRole("button");
+ expect(button).toBeInTheDocument();
+ expect(button).toBeEnabled();
+ expect(button.getAttribute("aria-description")).toMatch(/no chain/i);
+ });
+
+ it("renders correctly with chain selected", () => {
+ render(
+
+ );
+
+ const button = screen.getByRole("button");
+ expect(button).toBeInTheDocument();
+ expect(button.getAttribute("aria-description")).toContain(mockChain.name);
+
+ const chainName = screen.getByText(mockChain.name);
+ expect(chainName).toBeInTheDocument();
+
+ const chainImage = screen.getByAltText(`${mockChain.name}`, {
+ exact: false,
+ });
+ expect(chainImage).toBeInTheDocument();
+ expect(chainImage).toHaveAttribute(
+ "style",
+ `background-image: url(${mockChain.iconUrl});`
+ );
+ });
+
+ it("calls onClick when the component is clicked", () => {
+ const handleClick = jest.fn();
+ render(
+
+ );
+
+ const button = screen.getByRole("button");
+ fireEvent.click(button);
+ expect(handleClick).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/apps/namadillo/src/App/Transfer/__tests__/TransferDestination.test.tsx b/apps/namadillo/src/App/Transfer/__tests__/TransferDestination.test.tsx
new file mode 100644
index 000000000..fee5a7878
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/__tests__/TransferDestination.test.tsx
@@ -0,0 +1,100 @@
+import { fireEvent, render, screen } from "@testing-library/react";
+import {
+ namadaChainMock,
+ randomChainMock,
+} from "App/Transfer/__mocks__/chains";
+import { TransferDestination } from "App/Transfer/TransferDestination";
+import BigNumber from "bignumber.js";
+import { providerMock } from "../__mocks__/providers";
+
+describe("TransferDestination", () => {
+ it("should render the component with the default props", () => {
+ render();
+ expect(screen.getByText(/select chain/i)).toBeInTheDocument();
+ });
+
+ it("should render the TabSelector for shielded/transparent when onChangeShielded is provided", () => {
+ render(
+
+ );
+ expect(screen.getByText("Shielded")).toBeInTheDocument();
+ expect(screen.getByText("Transparent")).toBeInTheDocument();
+ });
+
+ it("should render a yellow border when transfer is shielded", () => {
+ const { container } = render();
+ expect(container.firstElementChild?.className).toContain("border-yellow");
+ });
+
+ it("should render nothing related to shielding when provided chain is not Namada", () => {
+ render(
+
+ );
+ expect(screen.queryByText(/shielded/i)).not.toBeInTheDocument();
+ });
+
+ it("should render correct chain name when shielded transfer is set", () => {
+ render(
+
+ );
+ expect(screen.getByText(/namada shielded/i)).toBeInTheDocument();
+ });
+
+ it("should render correct chain name when transparent transfer is set", () => {
+ render(
+
+ );
+ expect(screen.getByText(/namada transparent/i)).toBeInTheDocument();
+ });
+
+ it("should toggle between shielded and transparent", () => {
+ const onChangeShieldedMock = jest.fn();
+ render(
+
+ );
+ const transparentButton = screen.getByText("Transparent");
+ fireEvent.click(transparentButton);
+ expect(onChangeShieldedMock).toHaveBeenCalledWith(false);
+ });
+
+ it("should toggle between custom and my address when onToggleCustomAddress is provided", () => {
+ const onToggleCustomAddressMock = jest.fn();
+ render(
+
+ );
+ const customAddressButton = screen.getByText("Custom Address");
+ fireEvent.click(customAddressButton);
+ expect(onToggleCustomAddressMock).toHaveBeenCalledWith(true);
+ });
+
+ it("should display the transaction fee if provided", () => {
+ const fee = new BigNumber(0.01);
+ render();
+ const transactionFee = screen.getByText("Transaction Fee");
+ expect(transactionFee).toBeInTheDocument();
+ expect(transactionFee.parentNode?.textContent).toContain("0.01");
+ });
+});
diff --git a/apps/namadillo/src/App/Transfer/__tests__/TransferSource.test.tsx b/apps/namadillo/src/App/Transfer/__tests__/TransferSource.test.tsx
new file mode 100644
index 000000000..b21c3c2c0
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/__tests__/TransferSource.test.tsx
@@ -0,0 +1,87 @@
+import { fireEvent, render, screen } from "@testing-library/react";
+import {
+ TransferSource,
+ TransferSourceProps,
+} from "App/Transfer/TransferSource";
+import BigNumber from "bignumber.js";
+import { namadaChainMock } from "../__mocks__/chains";
+import { providerConnectedMock } from "../__mocks__/providers";
+
+describe("Component: TransferSource", () => {
+ it("should render the component with the default props", () => {
+ render(
+
+ );
+ expect(screen.getByText("Connect Wallet")).toBeInTheDocument();
+ expect(screen.getByText(/select chain/i)).toBeInTheDocument();
+ });
+
+ const setup = (props: Partial = {}): void => {
+ render(
+
+ );
+ };
+
+ const getEmptyChain = (): HTMLElement => {
+ return screen.getByText(/select chain/i);
+ };
+
+ const getEmptyAsset = (): HTMLElement => {
+ return screen.getByText(/asset/i);
+ };
+
+ it("should call onConnectProvider when Connect Wallet button is clicked", () => {
+ const onConnectProviderMock = jest.fn();
+ setup({ openProviderSelector: onConnectProviderMock });
+ fireEvent.click(screen.getByText("Connect Wallet"));
+ expect(onConnectProviderMock).toHaveBeenCalled();
+ });
+
+ it("should call openChainSelector when the SelectedChain is clicked", () => {
+ const openChainSelectorMock = jest.fn();
+ setup({
+ openChainSelector: openChainSelectorMock,
+ provider: providerConnectedMock,
+ });
+ const chain = getEmptyChain();
+ fireEvent.click(chain);
+ expect(openChainSelectorMock).toHaveBeenCalled();
+ });
+
+ it("should render controls disabled when chain is not defined", () => {
+ const openAssetSelectorMock = jest.fn();
+ setup({ openAssetSelector: openAssetSelectorMock });
+ const assetControl = getEmptyAsset();
+ fireEvent.click(assetControl);
+ expect(openAssetSelectorMock).not.toHaveBeenCalled();
+ const amountInput = screen.getByDisplayValue("0");
+ expect(amountInput).toBeDisabled();
+ });
+
+ it("should call openAssetSelector when the SelectedAsset is clicked", () => {
+ const openAssetSelectorMock = jest.fn();
+ setup({ openAssetSelector: openAssetSelectorMock, chain: namadaChainMock });
+ const assetControl = getEmptyAsset();
+ fireEvent.click(assetControl);
+ expect(openAssetSelectorMock).toHaveBeenCalled();
+ });
+
+ it("should render the amount input with the correct value", () => {
+ const amount = new BigNumber(100);
+ setup({ amount });
+ const amountInput = screen.getByDisplayValue("100");
+ expect(amountInput).toBeInTheDocument();
+ });
+
+ it("should call onChangeAmount when the amount input is changed", () => {
+ const onChangeAmountMock = jest.fn();
+ setup({ amount: new BigNumber(0), onChangeAmount: onChangeAmountMock });
+ const amountInput = screen.getByDisplayValue("0");
+ fireEvent.change(amountInput, { target: { value: new BigNumber("200") } });
+ expect(onChangeAmountMock).toHaveBeenCalled();
+ });
+});
diff --git a/apps/namadillo/src/App/Transfer/assets/namada-shielded.svg b/apps/namadillo/src/App/Transfer/assets/namada-shielded.svg
new file mode 100644
index 000000000..8d1e160ab
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/assets/namada-shielded.svg
@@ -0,0 +1 @@
+
diff --git a/apps/namadillo/src/App/Transfer/assets/namada-transparent.svg b/apps/namadillo/src/App/Transfer/assets/namada-transparent.svg
new file mode 100644
index 000000000..5218e65ad
--- /dev/null
+++ b/apps/namadillo/src/App/Transfer/assets/namada-transparent.svg
@@ -0,0 +1 @@
+
diff --git a/apps/namadillo/src/App/Transfer/routes.ts b/apps/namadillo/src/App/Transfer/routes.ts
index b0fd05652..524459c38 100644
--- a/apps/namadillo/src/App/Transfer/routes.ts
+++ b/apps/namadillo/src/App/Transfer/routes.ts
@@ -10,7 +10,9 @@ export const namTransfer = (): RouteOutput => routeOutput("/nam");
export const shield = (): RouteOutput => routeOutput("/shield");
-export const shieldAll = (): RouteOutput => routeOutput("/shield-all");
+export const shieldAll = (): RouteOutput => routeOutput(`/shield-all`);
+
+export const ibcTransfer = (): RouteOutput => routeOutput(`/ibc`);
export default {
index,
@@ -18,4 +20,5 @@ export default {
namTransfer,
shield,
shieldAll,
+ ibcTransfer,
};
diff --git a/apps/namadillo/src/atoms/accounts/atoms.ts b/apps/namadillo/src/atoms/accounts/atoms.ts
index 40957ef08..e483d972b 100644
--- a/apps/namadillo/src/atoms/accounts/atoms.ts
+++ b/apps/namadillo/src/atoms/accounts/atoms.ts
@@ -7,6 +7,7 @@ import { namadaExtensionConnectedAtom } from "atoms/settings";
import { queryDependentFn } from "atoms/utils";
import BigNumber from "bignumber.js";
import { atomWithMutation, atomWithQuery } from "jotai-tanstack-query";
+import { chainConfigByName } from "registry";
import {
fetchAccountBalance,
fetchAccounts,
@@ -50,6 +51,7 @@ export const accountBalanceAtom = atomWithQuery((get) => {
const tokenAddress = get(nativeTokenAddressAtom);
const enablePolling = get(shouldUpdateBalanceAtom);
const api = get(indexerApiAtom);
+ const chainConfig = chainConfigByName("namada");
return {
// TODO: subscribe to indexer events when it's done
@@ -59,7 +61,10 @@ export const accountBalanceAtom = atomWithQuery((get) => {
return await fetchAccountBalance(
api,
defaultAccount.data,
- tokenAddress.data!
+ tokenAddress.data!,
+ // As this is a nam balance specific atom, we can safely assume that the
+ // first currency is the native token
+ chainConfig.currencies[0].coinDecimals
);
}, [tokenAddress, defaultAccount]),
};
diff --git a/apps/namadillo/src/atoms/accounts/services.ts b/apps/namadillo/src/atoms/accounts/services.ts
index 453fb0ba9..44e546441 100644
--- a/apps/namadillo/src/atoms/accounts/services.ts
+++ b/apps/namadillo/src/atoms/accounts/services.ts
@@ -17,12 +17,13 @@ export const fetchDefaultAccount = async (): Promise => {
export const fetchAccountBalance = async (
api: DefaultApi,
account: Account | undefined,
- tokenAddress: string
+ tokenAddress: string,
+ decimals: number
): Promise => {
if (!account) return BigNumber(0);
const balancesResponse = await api.apiV1AccountAddressGet(account.address);
- const balances = balancesResponse.data
+ const balance = balancesResponse.data
// TODO: add filter to the api call
.filter(({ tokenAddress: ta }) => ta === tokenAddress)
.map(({ tokenAddress, balance }) => {
@@ -30,8 +31,10 @@ export const fetchAccountBalance = async (
token: tokenAddress,
amount: balance,
};
- });
+ })
+ .at(0);
- if (balances.length === 0) return BigNumber(0);
- return new BigNumber(balances[0].amount || 0);
+ return balance ?
+ BigNumber(balance.amount).shiftedBy(-decimals)
+ : BigNumber(0);
};
diff --git a/apps/namadillo/src/atoms/staking/atoms.ts b/apps/namadillo/src/atoms/staking/atoms.ts
index fef4c722e..8e9e5796b 100644
--- a/apps/namadillo/src/atoms/staking/atoms.ts
+++ b/apps/namadillo/src/atoms/staking/atoms.ts
@@ -96,21 +96,6 @@ export const createWithdrawTxAtomFamily = atomFamily((id: string) => {
});
});
-export const claimRewardsAtom = atomWithMutation((get) => {
- const chain = get(chainAtom);
- return {
- mutationKey: ["create-claim-tx"],
- enabled: chain.isSuccess,
- mutationFn: async ({
- params,
- gasConfig,
- account,
- }: BuildTxAtomParams) => {
- return createClaimTx(chain.data!, account, params, gasConfig);
- },
- };
-});
-
export const claimableRewardsAtom = atomWithQuery((get) => {
const account = get(defaultAccountAtom);
const chainParameters = get(chainParametersAtom);
@@ -134,6 +119,21 @@ export const claimableRewardsAtom = atomWithQuery((get) => {
};
});
+export const claimRewardsAtom = atomWithMutation((get) => {
+ const chain = get(chainAtom);
+ return {
+ mutationKey: ["create-claim-tx"],
+ enabled: chain.isSuccess,
+ mutationFn: async ({
+ params,
+ gasConfig,
+ account,
+ }: BuildTxAtomParams) => {
+ return createClaimTx(chain.data!, account, params, gasConfig);
+ },
+ };
+});
+
export const claimAndStakeRewardsAtom = atomWithMutation((get) => {
const chain = get(chainAtom);
const claimableRewards = get(claimableRewardsAtom);
diff --git a/apps/namadillo/src/atoms/staking/services.ts b/apps/namadillo/src/atoms/staking/services.ts
index 7016e3f69..d8523b6cd 100644
--- a/apps/namadillo/src/atoms/staking/services.ts
+++ b/apps/namadillo/src/atoms/staking/services.ts
@@ -12,6 +12,7 @@ import {
WithdrawProps,
WrapperTxProps,
} from "@namada/types";
+import { queryClient } from "App/Common/QueryProvider";
import { getSdkInstance } from "hooks";
import { TransactionPair, buildTxPair } from "lib/query";
import { Address, AddressBalance, ChainSettings, GasConfig } from "types";
@@ -159,3 +160,11 @@ export const createClaimAndStakeTx = async (
account.address
);
};
+
+export const clearClaimRewards = (accountAddress: string): void => {
+ const emptyClaimRewards = {};
+ queryClient.setQueryData(
+ ["claim-rewards", accountAddress],
+ () => emptyClaimRewards
+ );
+};
diff --git a/apps/namadillo/src/hooks/useTransactionCallbacks.tsx b/apps/namadillo/src/hooks/useTransactionCallbacks.tsx
index 7d736b189..be00e6a82 100644
--- a/apps/namadillo/src/hooks/useTransactionCallbacks.tsx
+++ b/apps/namadillo/src/hooks/useTransactionCallbacks.tsx
@@ -1,29 +1,36 @@
-import { accountBalanceAtom } from "atoms/accounts";
+import { accountBalanceAtom, defaultAccountAtom } from "atoms/accounts";
import { shouldUpdateBalanceAtom, shouldUpdateProposalAtom } from "atoms/etc";
-import { claimableRewardsAtom } from "atoms/staking";
+import { claimableRewardsAtom, clearClaimRewards } from "atoms/staking";
import { useAtomValue, useSetAtom } from "jotai";
import { useTransactionEventListener } from "utils";
export const useTransactionCallback = (): void => {
const { refetch: refetchBalances } = useAtomValue(accountBalanceAtom);
const { refetch: refetchRewards } = useAtomValue(claimableRewardsAtom);
+ const { data: account } = useAtomValue(defaultAccountAtom);
const shouldUpdateBalance = useSetAtom(shouldUpdateBalanceAtom);
const onBalanceUpdate = (): void => {
// TODO: refactor this after event subscription is enabled on indexer
shouldUpdateBalance(true);
refetchBalances();
- refetchRewards();
const timePolling = 6 * 1000;
setTimeout(() => shouldUpdateBalance(false), timePolling);
+
+ if (account?.address) {
+ clearClaimRewards(account.address);
+ setTimeout(() => refetchRewards(), timePolling);
+ }
};
useTransactionEventListener("Bond.Success", onBalanceUpdate);
useTransactionEventListener("Unbond.Success", onBalanceUpdate);
useTransactionEventListener("Withdraw.Success", onBalanceUpdate);
useTransactionEventListener("Redelegate.Success", onBalanceUpdate);
- useTransactionEventListener("ClaimRewards.Success", onBalanceUpdate);
+ useTransactionEventListener("ClaimRewards.Success", onBalanceUpdate, [
+ account?.address,
+ ]);
const shouldUpdateProposal = useSetAtom(shouldUpdateProposalAtom);
diff --git a/apps/namadillo/src/registry/cosmoshub.json b/apps/namadillo/src/registry/cosmoshub.json
new file mode 100644
index 000000000..f3e605021
--- /dev/null
+++ b/apps/namadillo/src/registry/cosmoshub.json
@@ -0,0 +1,22 @@
+{
+ "chainName": "Cosmos Hub",
+ "currencies": [
+ {
+ "coinDecimals": 6,
+ "coinDenom": "ATOM",
+ "coinMinimalDenom": "uatom"
+ }
+ ],
+ "feeCurrencies": [
+ {
+ "coinDecimals": 6,
+ "coinDenom": "ATOM",
+ "coinMinimalDenom": "uatom"
+ }
+ ],
+ "stakeCurrency": {
+ "coinDecimals": 6,
+ "coinDenom": "ATOM",
+ "coinMinimalDenom": "uatom"
+ }
+}
diff --git a/apps/namadillo/src/registry/index.ts b/apps/namadillo/src/registry/index.ts
new file mode 100644
index 000000000..86b83d5cf
--- /dev/null
+++ b/apps/namadillo/src/registry/index.ts
@@ -0,0 +1,54 @@
+import cosmoshub from "./cosmoshub.json";
+import namada from "./namada.json";
+
+type MinimalDenom = string;
+type ConfigName = string;
+type ChainName = "cosmoshub" | "namada";
+type ChainMinDenom = "uatom" | "namnam";
+
+type Currency = {
+ coinDecimals: number;
+ coinDenom: string;
+ coinMinimalDenom: string;
+};
+
+export type ChainConfig = {
+ currencies: Currency[];
+ feeCurrencies: Currency[];
+ stakeCurrency: Currency;
+};
+
+const loadedConfigs: ChainConfig[] = [];
+const minimalDenomMap: Map = new Map();
+const nameMap: Map = new Map();
+
+export function chainConfigByMinDenom(minDenom: ChainMinDenom): ChainConfig {
+ const index = minimalDenomMap.get(minDenom);
+ if (typeof index === "undefined") {
+ throw new Error("Chain config not found");
+ }
+
+ return loadedConfigs[index];
+}
+
+export function chainConfigByName(name: ChainName): ChainConfig {
+ const index = nameMap.get(name);
+ if (typeof index === "undefined") {
+ throw new Error("Chain config not found");
+ }
+ return loadedConfigs[index];
+}
+
+function loadConfigs(configs: [MinimalDenom, ConfigName, ChainConfig][]): void {
+ configs.forEach(([minimalDenom, name, config], index) => {
+ loadedConfigs.push(config);
+
+ minimalDenomMap.set(minimalDenom, index);
+ nameMap.set(name, index);
+ });
+}
+
+loadConfigs([
+ ["namnam", "namada", namada],
+ ["uatom", "cosmoshub", cosmoshub],
+]);
diff --git a/apps/namadillo/src/registry/namada.json b/apps/namadillo/src/registry/namada.json
new file mode 100644
index 000000000..7ec080145
--- /dev/null
+++ b/apps/namadillo/src/registry/namada.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/types.d.ts b/apps/namadillo/src/types.d.ts
index bcc61d059..8f18d4a37 100644
--- a/apps/namadillo/src/types.d.ts
+++ b/apps/namadillo/src/types.d.ts
@@ -166,3 +166,24 @@ export type ToastNotification = {
export type ToastNotificationEntryFilter = (
notification: ToastNotification
) => boolean;
+
+export type Provider = {
+ name: string;
+ iconUrl: string;
+ connected: boolean;
+};
+
+export type Chain = {
+ chainId: string;
+ name: string;
+ iconUrl: string;
+};
+
+export type Asset = {
+ chain: Chain;
+ name: string;
+ iconUrl: string;
+ denomination: string;
+ minimalDenomination: string;
+ decimals: number;
+};
diff --git a/apps/namadillo/src/utils/index.ts b/apps/namadillo/src/utils/index.ts
index b6d16d159..bc701aa18 100644
--- a/apps/namadillo/src/utils/index.ts
+++ b/apps/namadillo/src/utils/index.ts
@@ -36,14 +36,15 @@ export const proposalIdToString = (proposalId: bigint): string =>
export const useTransactionEventListener = (
event: T,
- handler: (this: Window, ev: WindowEventMap[T]) => void
+ handler: (this: Window, ev: WindowEventMap[T]) => void,
+ deps: React.DependencyList = []
): void => {
useEffect(() => {
window.addEventListener(event, handler);
return () => {
window.removeEventListener(event, handler);
};
- }, []);
+ }, deps);
};
const secondsToDateTime = (seconds: bigint): DateTime =>
diff --git a/apps/namadillo/vite.config.mjs b/apps/namadillo/vite.config.mjs
index 618c482cf..a6d1ad14a 100644
--- a/apps/namadillo/vite.config.mjs
+++ b/apps/namadillo/vite.config.mjs
@@ -1,22 +1,11 @@
/* eslint-disable */
import react from "@vitejs/plugin-react";
-import { defineConfig, loadEnv } from "vite";
+import { defineConfig } from "vite";
import checker from "vite-plugin-checker";
import { nodePolyfills } from "vite-plugin-node-polyfills";
import tsconfigPaths from "vite-tsconfig-paths";
-export default defineConfig(({ mode }) => {
- const env = loadEnv(mode, process.cwd(), "");
- const filteredEnv = Object.keys(env).reduce((acc, current) => {
- if (current.startsWith("NAMADA_INTERFACE_")) {
- return {
- ...acc,
- [current]: env[current],
- };
- }
- return acc;
- }, {});
-
+export default defineConfig(() => {
return {
plugins: [
react(),
@@ -30,9 +19,6 @@ export default defineConfig(({ mode }) => {
overlay: { initialIsOpen: false },
}),
],
- define: {
- "process.env": filteredEnv,
- },
optimizeDeps: {
esbuildOptions: {
// Node.js global to browser globalThis
diff --git a/docker/faucet/nginx.conf b/docker/faucet/nginx.conf
index 51acaff2f..a4476297d 100644
--- a/docker/faucet/nginx.conf
+++ b/docker/faucet/nginx.conf
@@ -6,6 +6,5 @@ server {
index index.html index.htm;
try_files $uri $uri/ $uri.html /index.html;
}
- gzip on;
- gzip_types text/plain text/css application/javascript application/json application/vnd.ms-fontobject application/xml+rss application/atom+xml font/opentype font/ttf image/svg+xml;
+ gzip off;
}
diff --git a/docker/namadillo/nginx.conf b/docker/namadillo/nginx.conf
index 51acaff2f..a4476297d 100644
--- a/docker/namadillo/nginx.conf
+++ b/docker/namadillo/nginx.conf
@@ -6,6 +6,5 @@ server {
index index.html index.htm;
try_files $uri $uri/ $uri.html /index.html;
}
- gzip on;
- gzip_types text/plain text/css application/javascript application/json application/vnd.ms-fontobject application/xml+rss application/atom+xml font/opentype font/ttf image/svg+xml;
+ gzip off;
}
diff --git a/packages/components/src/AmountInput.tsx b/packages/components/src/AmountInput.tsx
index 0f3b93d95..7d72d80ff 100644
--- a/packages/components/src/AmountInput.tsx
+++ b/packages/components/src/AmountInput.tsx
@@ -9,9 +9,11 @@ export type BigNumberElement = Omit & {
value?: BigNumber;
};
+export type ChangeAmountEvent = ChangeEventHandler;
+
type Props = Omit & {
value?: BigNumber;
- onChange?: ChangeEventHandler;
+ onChange?: ChangeAmountEvent;
maxDecimalPlaces?: number;
min?: string | number | BigNumber;
max?: string | number | BigNumber;
diff --git a/packages/components/src/Input.tsx b/packages/components/src/Input.tsx
index c724efb12..71e51d69e 100644
--- a/packages/components/src/Input.tsx
+++ b/packages/components/src/Input.tsx
@@ -171,7 +171,11 @@ export const Input = ({
return (
);
diff --git a/packages/components/src/Modal.tsx b/packages/components/src/Modal.tsx
index 9844298b9..23dc28954 100644
--- a/packages/components/src/Modal.tsx
+++ b/packages/components/src/Modal.tsx
@@ -45,6 +45,7 @@ export const Modal = ({
/>