Skip to content

Commit

Permalink
Transfer Module - Part 2 (#1131)
Browse files Browse the repository at this point in the history
* feat(namadillo): disabling fields on transfer module

* feat(namadillo): starting integration with keplr wallet

* feat: adding chain registry package and updating types and tests

* feat(namadillo): writing tests for SelectChainModal

* feat(namadillo): first chain modal version

* feat: adding new features to transfer module

* feat: adding more tests
  • Loading branch information
pedrorezende authored Sep 30, 2024
1 parent 1def6e0 commit a20d53b
Show file tree
Hide file tree
Showing 51 changed files with 1,402 additions and 316 deletions.
1 change: 1 addition & 0 deletions apps/namadillo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
18 changes: 13 additions & 5 deletions apps/namadillo/src/App/Common/SelectModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<Modal onClose={onClose}>
<ModalTransition>
<div
className={clsx(
"px-3 pt-2 pb-6 bg-rblack max-w-[400px] min-h-[120px] w-screen rounded-xl border border-neutral-700"
className={twMerge(
clsx(
"px-8 pt-3 pb-6 bg-rblack max-w-[480px] min-h-[120px]",
"w-screen rounded-xl border border-neutral-700"
),
className
)}
{...props}
>
<header className="flex w-full justify-center items-center relative mb-4 text-light leading-8">
<header className="flex w-full justify-center items-center relative mb-6 text-light leading-8">
{title}
<i
className="cursor-pointer text-white absolute right-0 text-xl p-1.5 hover:text-yellow z-50"
className="cursor-pointer text-white absolute -right-2.5 text-xl p-1.5 hover:text-yellow z-50"
onClick={onClose}
>
<IoClose />
Expand Down
40 changes: 20 additions & 20 deletions apps/namadillo/src/App/Common/TabSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,25 @@ export const TabSelector = ({
onChange,
}: TabSelectorProps): JSX.Element => {
return (
<nav>
<ul className="flex">
{items.map((item) => (
<li key={item.id} className="w-full">
<button
onClick={() => onChange(item)}
className={twMerge(
clsx(
"border border-current text-current rounded-sm bg-black opacity-70",
{ "border border-current opacity-100": item.id === active },
item.className
)
)}
>
{item.text}
</button>
</li>
))}
</ul>
</nav>
<ul className="flex">
{items.map((item) => (
<li key={item.id} className="w-full">
<button
type="button"
onClick={() => onChange(item)}
className={twMerge(
clsx(
"w-full text-current rounded-sm bg-black opacity-50 py-1",
"hover:opacity-80 transition-opacity duration-200",
{ "border border-current opacity-100": item.id === active },
item.className
)
)}
>
{item.text}
</button>
</li>
))}
</ul>
);
};
31 changes: 31 additions & 0 deletions apps/namadillo/src/App/Transfer/AssetCard.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<span
className={clsx(
"grid grid-cols-[40px_auto] gap-6 w-full px-4 py-2.5 items-center"
)}
>
{image ?
<img
src={image}
alt={asset.name + ` logo`}
className="w-full aspect-square"
/>
: <img
className="bg-neutral-800 rounded-full aspect-square w-full"
alt="Logo not available"
role="img"
/>
}
<span className="text-left">{asset.name}</span>
</span>
);
};
3 changes: 2 additions & 1 deletion apps/namadillo/src/App/Transfer/AvailableAmountFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const AvailableAmountFooter = ({
return <></>;
}

// TODO: Replace <Currency /> usage here
return (
<div
className={clsx(
Expand All @@ -28,7 +29,7 @@ export const AvailableAmountFooter = ({
Available:
<Currency
amount={availableAmount}
currency={currency}
currency="nam"
spaceAroundSign={true}
currencyPosition="right"
/>
Expand Down
19 changes: 19 additions & 0 deletions apps/namadillo/src/App/Transfer/ChainCard.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<span
className={clsx(
"grid grid-cols-[40px_auto] gap-6 w-full px-4 py-2.5 items-center"
)}
>
<img src={chain.logo_URIs?.svg} alt={chain.pretty_name + " logo"} />
<span className="text-left">{chain.pretty_name}</span>
</span>
);
};
2 changes: 1 addition & 1 deletion apps/namadillo/src/App/Transfer/ConnectProviderButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const ConnectProviderButton = ({
return (
<ActionButton
type="button"
className="inline-flex absolute top-0 right-0 w-auto text-xs px-2 py-px"
className="inline-flex top-0 right-0 w-auto text-xs px-2 py-px"
onClick={onClick}
size="xs"
backgroundColor="white"
Expand Down
125 changes: 125 additions & 0 deletions apps/namadillo/src/App/Transfer/Example.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import BigNumber from "bignumber.js";
import { useEffect, useState } from "react";

// This can be loaded async using @chain-registry/client
import {
chain as celestia,
assets as celestiaAssets,
} from "chain-registry/mainnet/celestia";
import {
chain as cosmos,
assets as cosmosAssets,
} from "chain-registry/mainnet/cosmoshub";
import {
chain as dydx,
assets as dydxAssets,
} from "chain-registry/mainnet/dydx";
import {
chain as osmosis,
assets as osmosisAssets,
} from "chain-registry/mainnet/osmosis";
import {
chain as stargaze,
assets as stargazeAssets,
} from "chain-registry/mainnet/stargaze";
import {
chain as stride,
assets as strideAssets,
} from "chain-registry/mainnet/stride";

// This will be replaced by namada registry in the future
import namadaChain from "registry/namada.json";

import { Asset, Chain } from "@chain-registry/types";
import { Panel } from "@namada/components";
import { integrations } from "@namada/integrations";
import { selectedIBCChainAtom, selectedIBCWallet } from "atoms/integrations";
import { wallets } from "integrations";
import { useAtom } from "jotai";
import { useMemo } from "react";
import { WalletProvider } from "types";
import { TransferModule } from "./TransferModule";

export const Example = (): JSX.Element => {
const [selectedWallet, setWallet] = useAtom(selectedIBCWallet);
const [chainId, setChainId] = useAtom(selectedIBCChainAtom);
const [selectedAsset, setSelectedAsset] = useState<Asset>();
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<string, Chain> = 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 (
<Panel className="py-20">
<TransferModule
isConnected={false}
onSubmitTransfer={() => {}}
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)}
/>
</Panel>
);
};
9 changes: 0 additions & 9 deletions apps/namadillo/src/App/Transfer/IBCFromNamadaModule.tsx

This file was deleted.

12 changes: 0 additions & 12 deletions apps/namadillo/src/App/Transfer/IBCTransfers.tsx

This file was deleted.

61 changes: 61 additions & 0 deletions apps/namadillo/src/App/Transfer/SelectAssetModal.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<SelectModal title="Select Asset" onClose={onClose}>
<div className="mb-4">
<Search placeholder="Search chain" onChange={setFilter} />
</div>
<Stack
as="ul"
gap={0}
className="max-h-[400px] overflow-auto dark-scrollbar pb-4 mr-[-0.5rem]"
>
{filteredAssets.map((asset: Asset, index: number) => (
<li key={index} className="text-sm">
<button
onClick={() => {
onSelect(asset);
onClose();
}}
className={twMerge(
clsx(
"w-full rounded-sm border border-transparent",
"hover:border-neutral-400 transition-colors duration-150"
)
)}
>
<AssetCard asset={asset} />
</button>
</li>
))}
</Stack>
</SelectModal>
);
};
Loading

1 comment on commit a20d53b

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.