Skip to content

Commit

Permalink
Namadillo: Reviewing transaction timelines and fixing a few things (#…
Browse files Browse the repository at this point in the history
…1365)

* refactor: simplifying mapCoinsToAssets

* feat: retrieving asset in case chain doesn't have a channel on registry

* fix: fixing styles for disabled buttons

* fix: keeping ibc channel text inputs when a value is set

* feat: reviewing transaction timelines

* feat: if ibc channels are not defined, display the text field
  • Loading branch information
pedrorezende authored Dec 3, 2024
1 parent 632aee0 commit 2b418e3
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 126 deletions.
14 changes: 8 additions & 6 deletions apps/namadillo/src/App/Ibc/IbcTransfer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,10 @@ export const IbcTransfer: React.FC = () => {
connectToChainId(chain.chain_id);
};

const requiresIbcChannels =
!ibcChannels?.cosmosChannelId ||
(shielded && !ibcChannels?.namadaChannelId);

return (
<>
<div className="relative min-h-[600px]">
Expand Down Expand Up @@ -253,6 +257,7 @@ export const IbcTransfer: React.FC = () => {
transactionFee={transactionFee}
isSubmitting={performIbcTransfer.isPending}
isIbcTransfer={true}
requiresIbcChannels={requiresIbcChannels}
ibcOptions={{
sourceChannel,
onChangeSourceChannel: setSourceChannel,
Expand All @@ -266,12 +271,9 @@ export const IbcTransfer: React.FC = () => {
)}
{transaction && (
<div
className={clsx(
"absolute z-50 py-12 left-0 top-0 w-full h-full bg-black",
{
"text-yellow": shielded,
}
)}
className={clsx("absolute z-50 py-12 left-0 top-0 w-full h-full", {
"text-yellow": shielded,
})}
>
<TransferTransactionTimeline transaction={transaction} />
</div>
Expand Down
9 changes: 4 additions & 5 deletions apps/namadillo/src/App/Ibc/IbcWithdraw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ export const IbcWithdraw: React.FC = () => {
connectToChainId(chain.chain_id);
};

const requiresIbcChannels = !ibcChannels?.cosmosChannelId;

return (
<div className="relative min-h-[600px]">
{!transaction && (
Expand Down Expand Up @@ -199,6 +201,7 @@ export const IbcWithdraw: React.FC = () => {
}}
isSubmitting={isPending}
isIbcTransfer={true}
requiresIbcChannels={requiresIbcChannels}
ibcOptions={{
sourceChannel,
onChangeSourceChannel: setSourceChannel,
Expand All @@ -210,11 +213,7 @@ export const IbcWithdraw: React.FC = () => {
</>
)}
{transaction && (
<div
className={clsx(
"absolute z-50 py-12 left-0 top-0 w-full h-full bg-black"
)}
>
<div className={clsx("absolute z-50 py-12 left-0 top-0 w-full h-full")}>
<TransferTransactionTimeline transaction={transaction} />
</div>
)}
Expand Down
99 changes: 21 additions & 78 deletions apps/namadillo/src/App/NamadaTransfer/NamadaTransfer.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Chain } from "@chain-registry/types";
import { Panel } from "@namada/components";
import { AccountType } from "@namada/types";
import { Timeline } from "App/Common/Timeline";
import { params } from "App/routes";
import { TransferTransactionTimeline } from "App/Transactions/TransferTransactionTimeline";
import { isShieldedAddress } from "App/Transfer/common";
import {
OnSubmitTransferParams,
Expand All @@ -20,10 +20,10 @@ import {
createUnshieldingTransferAtom,
} from "atoms/transfer/atoms";
import BigNumber from "bignumber.js";
import clsx from "clsx";
import { useTransaction } from "hooks/useTransaction";
import { useTransactionActions } from "hooks/useTransactionActions";
import { wallets } from "integrations";
import { getAssetImageUrl } from "integrations/utils";
import { useAtomValue } from "jotai";
import { createTransferDataFromNamada } from "lib/transactions";
import { useEffect, useMemo, useState } from "react";
Expand All @@ -36,15 +36,14 @@ import {
PartialTransferTransactionData,
TransferStep,
} from "types";
import { isNamadaAsset, useTransactionEventListListener } from "utils";
import { isNamadaAsset } from "utils";
import { NamadaTransferTopHeader } from "./NamadaTransferTopHeader";

export const NamadaTransfer: React.FC = () => {
const [searchParams, setSearchParams] = useSearchParams();
const [displayAmount, setDisplayAmount] = useState<BigNumber | undefined>();
const [shielded, setShielded] = useState<boolean>(true);
const [customAddress, setCustomAddress] = useState<string>("");
const [currentStep, setCurrentStep] = useState(0);
const [generalErrorMessage, setGeneralErrorMessage] = useState("");
const [transaction, setTransaction] =
useState<PartialTransferTransactionData>();
Expand Down Expand Up @@ -100,9 +99,6 @@ export const NamadaTransfer: React.FC = () => {
title: "Transfer transaction failed",
description: "",
}),
onSigned: () => {
setCurrentStep(1);
},
};

const transparentTransaction = useTransaction({
Expand Down Expand Up @@ -167,7 +163,6 @@ export const NamadaTransfer: React.FC = () => {

const isSourceShielded = isShieldedAddress(source);
const isTargetShielded = isShieldedAddress(target);
const assetImage = selectedAsset ? getAssetImageUrl(selectedAsset.asset) : "";

useEffect(() => {
if (transaction?.hash) {
Expand All @@ -178,18 +173,6 @@ export const NamadaTransfer: React.FC = () => {
}
}, [myTransactions]);

useTransactionEventListListener(
[
"TransparentTransfer.Success",
"ShieldedTransfer.Success",
"ShieldingTransfer.Success",
"UnshieldingTransfer.Success",
],
() => {
setCurrentStep(3);
}
);

const onChangeSelectedAsset = (address?: Address): void => {
setSearchParams(
(currentParams) => {
Expand All @@ -210,7 +193,6 @@ export const NamadaTransfer: React.FC = () => {
}: OnSubmitTransferParams): Promise<void> => {
try {
setGeneralErrorMessage("");
setCurrentStep(0);

if (typeof sourceAddress === "undefined") {
throw new Error("Source address is not defined");
Expand Down Expand Up @@ -253,30 +235,30 @@ export const NamadaTransfer: React.FC = () => {
const tx = txList[0];
setTransaction(tx);
storeTransaction(tx);
setCurrentStep(2);
} else {
throw "Invalid transaction response";
}
} catch (err) {
setGeneralErrorMessage(err + "");
setCurrentStep(0);
setTransaction(undefined);
}
};

return (
<Panel className="pt-8 pb-20">
<header className="flex flex-col items-center text-center mb-8 gap-6">
<h1 className={twMerge("text-lg", isSourceShielded && "text-yellow")}>
Transfer
</h1>
<NamadaTransferTopHeader
isSourceShielded={isSourceShielded}
isDestinationShielded={target ? isTargetShielded : undefined}
/>
</header>
<Panel className="relative pt-8 pb-20">
{!transaction && (
<div className="min-h-[600px]">
<header className="flex flex-col items-center text-center mb-8 gap-6">
<h1
className={twMerge("text-lg", isSourceShielded && "text-yellow")}
>
Transfer
</h1>
<NamadaTransferTopHeader
isSourceShielded={isSourceShielded}
isDestinationShielded={target ? isTargetShielded : undefined}
/>
</header>
<TransferModule
source={{
isLoadingAssets,
Expand Down Expand Up @@ -307,51 +289,12 @@ export const NamadaTransfer: React.FC = () => {
</div>
)}
{transaction && (
<div className={twMerge("my-12", isSourceShielded && "text-yellow")}>
<Timeline
currentStepIndex={currentStep}
steps={[
{
children: <img src={assetImage} className="w-14" />,
},
{
children: (
<div className={twMerge(isSourceShielded && "text-yellow")}>
Signature Required
</div>
),
bullet: true,
},
{
children: (
<>
<div>
Transfer to{" "}
{isTargetShielded ?
"Namada Shielded"
: "Namada Transparent"}
</div>
<div className="text-xs">{target}</div>
</>
),
bullet: true,
},
{
// TODO
children: (
<div
className={twMerge(
"flex flex-col items-center",
isTargetShielded && "text-yellow"
)}
>
<img src={assetImage} className="w-14 mb-2" />
Transfer Complete
</div>
),
},
]}
/>
<div
className={clsx("absolute z-50 py-12 left-0 top-0 w-full h-full", {
"text-yellow": shielded,
})}
>
<TransferTransactionTimeline transaction={transaction} />
</div>
)}
</Panel>
Expand Down
2 changes: 1 addition & 1 deletion apps/namadillo/src/App/Transfer/AvailableAmountFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const AvailableAmountFooter = ({
disabled={availableAmount.eq(0)}
onClick={onClickMax}
outlineColor="neutral"
className="text-neutral-500 text-xs py-0 px-3"
className="text-neutral-500 text-xs py-0 px-3 disabled:text-neutral-700"
backgroundHoverColor="white"
backgroundColor="transparent"
>
Expand Down
7 changes: 2 additions & 5 deletions apps/namadillo/src/App/Transfer/TransferModule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export type OnSubmitTransferParams = {
export type TransferModuleProps = {
source: TransferSourceProps;
destination: TransferDestinationProps;
requiresIbcChannels?: boolean;
transactionFee?: TransactionFee;
isSubmitting?: boolean;
errorMessage?: string;
Expand Down Expand Up @@ -94,6 +95,7 @@ export const TransferModule = ({
isSubmitting,
isIbcTransfer,
ibcOptions,
requiresIbcChannels,
onSubmitTransfer,
errorMessage,
}: TransferModuleProps): JSX.Element => {
Expand All @@ -113,11 +115,6 @@ export const TransferModule = ({
source.selectedAssetAddress
);

const requiresIbcChannels =
isIbcTransfer &&
(!ibcOptions?.sourceChannel ||
(destination.isShielded && !ibcOptions.destinationChannel));

const availableAmountMinusFees = useMemo(() => {
const { selectedAssetAddress, availableAmount } = source;

Expand Down
88 changes: 57 additions & 31 deletions apps/namadillo/src/atoms/integrations/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,15 @@ const tryDenomToIbcAsset = async (

const { path, baseDenom } = denomTrace;

const assetOnRegistry = tryDenomToRegistryAsset(
baseDenom,
cosmosRegistry.assets.map((assetListEl) => assetListEl.assets).flat()
);

if (assetOnRegistry) {
return assetOnRegistry;
}

// denom trace path may be something like...
// transfer/channel-16/transfer/channel-4353
// ...so from here walk the path to find the original chain
Expand Down Expand Up @@ -219,44 +228,61 @@ const unknownAsset = (denom: string): Asset => ({
symbol: denom,
});

const findOriginalAsset = async (
coin: Coin,
assets: Asset[],
ibcAddressToDenomTrace: (address: string) => Promise<DenomTrace | undefined>,
chainName?: string
): Promise<AddressWithAssetAndAmount> => {
const { minDenomAmount, denom } = coin;
let asset;

if (assets) {
asset = tryDenomToRegistryAsset(denom, assets);
}

if (!asset && chainName) {
asset = await tryDenomToIbcAsset(denom, ibcAddressToDenomTrace, chainName);
}

if (!asset) {
asset = unknownAsset(denom);
}

const baseBalance = BigNumber(minDenomAmount);
if (baseBalance.isNaN()) {
throw new Error(`Invalid balance: ${minDenomAmount}`);
}

const displayBalance = toDisplayAmount(asset, baseBalance);
return {
originalAddress: denom,
amount: displayBalance,
asset,
};
};

const findChainById = (chainId: string): Chain | undefined => {
return cosmosRegistry.chains.find((chain) => chain.chain_id === chainId);
};

export const mapCoinsToAssets = async (
coins: Coin[],
chainId: string,
ibcAddressToDenomTrace: (address: string) => Promise<DenomTrace | undefined>
): Promise<AddressWithAssetAndAmountMap> => {
const chainName = cosmosRegistry.chains.find(
(chain) => chain.chain_id === chainId
)?.chain_name;
const chainName = findChainById(chainId)?.chain_name;
const assets = mapUndefined(assetLookup, chainName);

const results = await Promise.allSettled(
coins.map(async (coin: Coin): Promise<AddressWithAssetAndAmount> => {
const { minDenomAmount, denom } = coin;

const asset =
typeof chainName === "undefined" || typeof assets === "undefined" ?
unknownAsset(denom)
: tryDenomToRegistryAsset(denom, assets) ||
(await tryDenomToIbcAsset(
denom,
ibcAddressToDenomTrace,
chainName
)) ||
unknownAsset(denom);

const baseBalance = BigNumber(minDenomAmount);
if (baseBalance.isNaN()) {
throw new Error(`Balance is invalid, got ${minDenomAmount}`);
}
// We always represent amounts in their display denom, so convert here
const displayBalance = toDisplayAmount(asset, baseBalance);

return {
originalAddress: denom,
amount: displayBalance,
asset,
};
})
coins.map(
async (coin) =>
await findOriginalAsset(
coin,
assets || [],
ibcAddressToDenomTrace,
chainName
)
)
);

const successfulResults = results.reduce<AddressWithAssetAndAmount[]>(
Expand Down

1 comment on commit 2b418e3

@github-actions
Copy link
Contributor

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.