Skip to content

Commit

Permalink
feat: add components to shield and unshield (#1249)
Browse files Browse the repository at this point in the history
  • Loading branch information
euharrison authored Nov 14, 2024
1 parent 14106eb commit 786429a
Show file tree
Hide file tree
Showing 13 changed files with 444 additions and 28 deletions.
30 changes: 20 additions & 10 deletions apps/namadillo/src/App/AccountOverview/TransparentOverviewPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { AtomErrorBoundary } from "App/Common/AtomErrorBoundary";
import { FiatCurrency } from "App/Common/FiatCurrency";
import { TableWithPaginator } from "App/Common/TableWithPaginator";
import { TokenCurrency } from "App/Common/TokenCurrency";
import { routes } from "App/routes";
import { params, routes } from "App/routes";
import { TokenBalance, transparentTokensAtom } from "atoms/balance/atoms";
import { getTotalDollar } from "atoms/balance/functions";
import { getAssetImageUrl } from "integrations/utils";
Expand All @@ -29,13 +29,20 @@ const TransparentTokensTable = ({

const headers = ["Token", { children: "Balance", className: "text-right" }];

const renderRow = ({ asset, balance, dollar }: TokenBalance): TableRow => {
const display = asset.display;
const renderRow = ({
originalAddress,
asset,
amount,
dollar,
}: TokenBalance): TableRow => {
const icon = getAssetImageUrl(asset);

return {
cells: [
<div key={`token-${display}`} className="flex items-center gap-4">
<div
key={`token-${originalAddress}`}
className="flex items-center gap-4"
>
<div className="aspect-square w-8 h-8">
{icon ?
<img src={icon} />
Expand All @@ -44,10 +51,10 @@ const TransparentTokensTable = ({
{asset.symbol}
</div>,
<div
key={`balance-${display}`}
key={`balance-${originalAddress}`}
className="flex flex-col text-right leading-tight"
>
<TokenCurrency asset={asset} amount={balance} />
<TokenCurrency asset={asset} amount={amount} />
{dollar && (
<FiatCurrency
className="text-neutral-600 text-sm"
Expand All @@ -56,13 +63,16 @@ const TransparentTokensTable = ({
)}
</div>,
<div
key={`buttons-${display}`}
key={`buttons-${originalAddress}`}
className="flex items-center justify-end gap-1"
>
<ActionButton size="xs" href={routes.maspShield}>
<ActionButton
size="xs"
href={`${routes.maspShield}?${params.asset}=${originalAddress}`}
>
Shield
</ActionButton>
{display === namadaAsset.display && (
{originalAddress === namadaAsset.address && (
<ActionButton
size="xs"
className="w-fit mx-auto"
Expand Down Expand Up @@ -135,7 +145,7 @@ const PanelContent = ({ data }: { data: TokenBalance[] }): JSX.Element => {
{
title: "Transparent NAM Balance",
amount: namBalance?.dollar,
namAmount: namBalance?.balance,
namAmount: namBalance?.amount,
button: (
<ActionButton
size="xs"
Expand Down
174 changes: 172 additions & 2 deletions apps/namadillo/src/App/Masp/MaspShield.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,179 @@
import { Chain } from "@chain-registry/types";
import { Panel } from "@namada/components";
import { TransactionTimeline } from "App/Common/TransactionTimeline";
import { params } from "App/routes";
import {
OnSubmitTransferParams,
TransferModule,
} from "App/Transfer/TransferModule";
import { allDefaultAccountsAtom } from "atoms/accounts";
import { namadaTransparentAssetsAtom } from "atoms/balance/atoms";
import { chainParametersAtom } from "atoms/chain/atoms";
import BigNumber from "bignumber.js";
import clsx from "clsx";
import { AnimatePresence, motion } from "framer-motion";
import { wallets } from "integrations";
import { getAssetImageUrl } from "integrations/utils";
import { useAtomValue } from "jotai";
import { useState } from "react";
import { useSearchParams } from "react-router-dom";
import namadaChain from "registry/namada.json";
import { Address } from "types";
import { MaspTopHeader } from "./MaspTopHeader";

export const MaspShield: React.FC = () => {
const [searchParams, setSearchParams] = useSearchParams();

const chainParameters = useAtomValue(chainParametersAtom);
const defaultAccounts = useAtomValue(allDefaultAccountsAtom);
const { data: availableAssets, isLoading: isLoadingBalances } = useAtomValue(
namadaTransparentAssetsAtom
);

const [currentStep, setCurrentStep] = useState(0);
const [generalErrorMessage, setGeneralErrorMessage] = useState("");

const chainId = chainParameters.data?.chainId;

const sourceAddress = defaultAccounts.data?.find(
(account) => !account.isShielded
)?.address;
const destinationAddress = defaultAccounts.data?.find(
(account) => account.isShielded
)?.address;

const selectedAssetAddress = searchParams.get(params.asset) || undefined;
const selectedAsset =
selectedAssetAddress ? availableAssets?.[selectedAssetAddress] : undefined;

const transactionFee =
selectedAsset ?
// TODO: remove hardcoding
{ ...selectedAsset, amount: BigNumber(0.03) }
: undefined;

const assetImage = selectedAsset ? getAssetImageUrl(selectedAsset.asset) : "";

const onChangeSelectedAsset = (address?: Address): void => {
setSearchParams(
(currentParams) => {
const newParams = new URLSearchParams(currentParams);
if (address) {
newParams.set(params.asset, address);
} else {
newParams.delete(params.asset);
}
return newParams;
},
{ replace: false }
);
};

const onSubmitTransfer = async ({
amount,
destinationAddress,
}: OnSubmitTransferParams): Promise<void> => {
try {
setGeneralErrorMessage("");
setCurrentStep(1);

if (typeof sourceAddress === "undefined") {
throw new Error("Source address is not defined");
}

if (!chainId) {
throw new Error("Chain ID is undefined");
}

if (!selectedAsset) {
throw new Error("No asset is selected");
}

if (typeof transactionFee === "undefined") {
throw new Error("No transaction fee is set");
}

// TODO do the transaction
alert(
"// TODO \n" + JSON.stringify({ amount, destinationAddress }, null, 2)
);

setCurrentStep(2);
} catch (err) {
setGeneralErrorMessage(err + "");
setCurrentStep(0);
}
};

return (
<Panel>
<div className="text-yellow">MASP: Shield (WIP)</div>
<Panel className="pt-8 pb-20">
<header className="flex flex-col items-center text-center mb-3 gap-6">
<h1 className="text-yellow text-lg">Shield</h1>
<MaspTopHeader type="shield" isShielded />
<h2 className="text-lg">Namada Transparent to Namada Shielded</h2>
</header>
<AnimatePresence>
{currentStep === 0 && (
<motion.div
key="transfer"
exit={{ opacity: 0 }}
className="min-h-[600px]"
>
<TransferModule
source={{
isLoadingAssets: isLoadingBalances,
availableAssets,
selectedAssetAddress,
availableAmount: selectedAsset?.amount,
chain: namadaChain as Chain,
availableWallets: [wallets.namada!],
wallet: wallets.namada,
walletAddress: sourceAddress,
onChangeSelectedAsset,
}}
destination={{
chain: namadaChain as Chain,
availableWallets: [wallets.namada!],
wallet: wallets.namada,
walletAddress: destinationAddress,
isShielded: true,
}}
transactionFee={transactionFee}
// TODO
// isSubmitting={something.isPending}
errorMessage={generalErrorMessage}
onSubmitTransfer={onSubmitTransfer}
/>
</motion.div>
)}
{currentStep > 0 && (
<motion.div
key="progress"
className={clsx("my-12 text-yellow")}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
>
<TransactionTimeline
currentStepIndex={currentStep}
steps={[
{
children: <img src={assetImage} className="w-14" />,
},
{ children: "Signature Required", bullet: true },
{
children: (
<>
<img src={assetImage} className="w-14 mb-2" />
Shielded Transfer Complete
</>
),
},
]}
/>
</motion.div>
)}
</AnimatePresence>
</Panel>
);
};
46 changes: 46 additions & 0 deletions apps/namadillo/src/App/Masp/MaspTopHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import clsx from "clsx";
import { twMerge } from "tailwind-merge";
import arrowImage from "./assets/arrow.svg";
import shieldedNamImage from "./assets/shielded-account.png";
import tokenImage from "./assets/token.svg";
import transparentNamImage from "./assets/transparent-account.png";

type MaspTopHeaderProps = {
type: "unshield" | "shield";
isShielded: boolean;
};

export const MaspTopHeader = ({
type,
isShielded,
}: MaspTopHeaderProps): JSX.Element => {
const namadaImage = isShielded ? shieldedNamImage : transparentNamImage;
const images =
type === "unshield" ?
[namadaImage, tokenImage, transparentNamImage]
: [transparentNamImage, tokenImage, namadaImage];

return (
<span className={"flex items-center gap-3 h-10"}>
<img
src={images[0]}
alt=""
className={twMerge(
clsx("w-5", {
"w-9 -mx-2": isShielded && type === "unshield",
})
)}
/>
<img src={arrowImage} alt="" />
<img src={images[1]} alt="" className="w-10" />
<img src={arrowImage} alt="" />
<img
src={images[2]}
className={twMerge(
clsx("w-5", { "w-9 -mx-2": isShielded && type === "shield" })
)}
alt=""
/>
</span>
);
};
Loading

1 comment on commit 786429a

@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.