Skip to content

Commit

Permalink
feat: listen for the transfer success event (#1490)
Browse files Browse the repository at this point in the history
  • Loading branch information
euharrison authored Jan 14, 2025
1 parent 786729b commit 41b7158
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 159 deletions.
2 changes: 1 addition & 1 deletion apps/namadillo/src/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export function App(): JSX.Element {
useExtensionEvents();
useTransactionNotifications();
useTransactionCallback();
useRegistryFeatures();
useTransactionWatcher();
useRegistryFeatures();
useServerSideEvents();

return (
Expand Down
7 changes: 4 additions & 3 deletions apps/namadillo/src/App/Common/SelectOptionModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Modal } from "@namada/components";
import { Fragment } from "react";
import { AssetsModalCard } from "./AssetsModalCard";
import { ModalContainer } from "./ModalContainer";

Expand Down Expand Up @@ -30,14 +31,14 @@ export const SelectOptionModal = ({
>
<ul className="grid grid-cols-[1fr_1px_1fr] gap-4 pt-1">
{items.map((item, index) => (
<>
<li key={index}>
<Fragment key={index}>
<li>
<AssetsModalCard {...item}>{item.children}</AssetsModalCard>
</li>
{index + 1 < items.length && (
<li className="w-px bg-white -my-1" />
)}
</>
</Fragment>
))}
</ul>
</ModalContainer>
Expand Down
37 changes: 8 additions & 29 deletions apps/namadillo/src/App/Common/Timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,42 +15,25 @@ type TransactionTimelineProps = {
complete?: boolean;
};

type DisabledProps = {
disabled: boolean;
};

const StepConnector = ({ disabled }: DisabledProps): JSX.Element => (
<i
className={clsx(
"h-10 w-px bg-current mb-3",
clsx({ "opacity-20": disabled })
)}
/>
const StepConnector = (): JSX.Element => (
<i className="h-10 w-px bg-current mb-3" />
);

const StepBullet = ({ disabled }: DisabledProps): JSX.Element => (
<i
className={clsx(
"w-4 aspect-square rounded-full bg-current",
clsx({ "opacity-20": disabled })
)}
/>
const StepBullet = (): JSX.Element => (
<i className="w-4 aspect-square rounded-full bg-current" />
);

const StepContent = ({
children,
isCurrentStep,
hasError,
disabled,
}: React.PropsWithChildren & {
isCurrentStep: boolean;
hasError: boolean;
disabled: boolean;
}): JSX.Element => (
<div
className={clsx("text-center", {
"animate-pulse": isCurrentStep && !hasError,
"opacity-20": disabled,
})}
>
{children}
Expand Down Expand Up @@ -206,19 +189,15 @@ export const Timeline = ({
className={twMerge(
clsx(
"flex flex-col gap-1 items-center",
"text-center transition-all duration-150"
"text-center transition-all duration-150",
{ "opacity-20": index > currentStepIndex && !hasError }
)
)}
>
{index > 0 && (
<StepConnector disabled={index > currentStepIndex} />
)}
{step.bullet && (
<StepBullet disabled={index > currentStepIndex} />
)}
{index > 0 && <StepConnector />}
{step.bullet && <StepBullet />}
<StepContent
isCurrentStep={index === currentStepIndex}
disabled={index > currentStepIndex}
hasError={!!hasError}
>
{step.children}
Expand Down
7 changes: 7 additions & 0 deletions apps/namadillo/src/atoms/transactions/atoms.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { defaultAccountAtom } from "atoms/accounts";
import { indexerApiAtom } from "atoms/api";
import { atom } from "jotai";
import { atomWithStorage } from "jotai/utils";
import { Address, TransferTransactionData } from "types";
import {
filterCompleteTransactions,
filterPendingTransactions,
} from "./functions";
import { fetchTransaction } from "./services";

export const transactionStorageKey = "namadillo:transactions";

Expand All @@ -31,3 +33,8 @@ export const completeTransactionsHistoryAtom = atom((get) => {
const myTransactions = get(myTransactionHistoryAtom);
return myTransactions.filter(filterCompleteTransactions);
});

export const fetchTransactionAtom = atom((get) => {
const api = get(indexerApiAtom);
return (hash: string) => fetchTransaction(api, hash);
});
10 changes: 10 additions & 0 deletions apps/namadillo/src/atoms/transactions/services.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { IndexedTx, StargateClient } from "@cosmjs/stargate";
import { DefaultApi, WrapperTransaction } from "@namada/indexer-client";
import { IbcTransferTransactionData } from "types";
import { sanitizeAddress } from "utils/address";

type SearchByTagsQuery = {
key: string;
Expand Down Expand Up @@ -57,3 +59,11 @@ export const queryForAck = async (
): Promise<IndexedTx[]> => {
return await client.searchTx(getAckPacketsParams(ibcTx));
};

export const fetchTransaction = async (
api: DefaultApi,
hash: string
): Promise<WrapperTransaction> => {
// indexer only accepts the hash as lowercase
return (await api.apiV1ChainWrapperTxIdGet(sanitizeAddress(hash))).data;
};
44 changes: 14 additions & 30 deletions apps/namadillo/src/hooks/useTransactionCallbacks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ import { shouldUpdateBalanceAtom, shouldUpdateProposalAtom } from "atoms/etc";
import { claimableRewardsAtom, clearClaimRewards } from "atoms/staking";
import { useAtomValue, useSetAtom } from "jotai";
import { TransferStep } from "types";
import {
useTransactionEventListener,
useTransactionEventListListener,
} from "utils";
import { EventData } from "types/events";
import { useTransactionEventListener } from "utils";
import { useTransactionActions } from "./useTransactionActions";

export const useTransactionCallback = (): void => {
Expand Down Expand Up @@ -35,9 +33,7 @@ export const useTransactionCallback = (): void => {
useTransactionEventListener("Unbond.Success", onBalanceUpdate);
useTransactionEventListener("Withdraw.Success", onBalanceUpdate);
useTransactionEventListener("Redelegate.Success", onBalanceUpdate);
useTransactionEventListener("ClaimRewards.Success", onBalanceUpdate, [
account?.address,
]);
useTransactionEventListener("ClaimRewards.Success", onBalanceUpdate);

useTransactionEventListener("VoteProposal.Success", () => {
shouldUpdateProposal(true);
Expand All @@ -48,28 +44,16 @@ export const useTransactionCallback = (): void => {
setTimeout(() => shouldUpdateProposal(false), timePolling);
});

useTransactionEventListListener(
[
"TransparentTransfer.Success",
"ShieldedTransfer.Success",
"ShieldingTransfer.Success",
"UnshieldingTransfer.Success",
],
(e) => {
e.detail.data.forEach((dataList) => {
dataList.data.forEach((props) => {
const sourceAddress = "source" in props ? props.source : "";
sourceAddress &&
changeTransaction(
e.detail.tx.hash,
{
status: "success",
currentStep: TransferStep.Complete,
},
sourceAddress
);
});
const onTransferSuccess = (e: EventData<unknown>): void => {
e.detail.tx.forEach(({ hash }) => {
changeTransaction(hash, {
status: "success",
currentStep: TransferStep.Complete,
});
}
);
});
};
useTransactionEventListener("TransparentTransfer.Success", onTransferSuccess);
useTransactionEventListener("ShieldedTransfer.Success", onTransferSuccess);
useTransactionEventListener("ShieldingTransfer.Success", onTransferSuccess);
useTransactionEventListener("UnshieldingTransfer.Success", onTransferSuccess);
};
125 changes: 55 additions & 70 deletions apps/namadillo/src/hooks/useTransactionNotifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@ import { searchAllStoredTxByHash } from "atoms/transactions";
import BigNumber from "bignumber.js";
import invariant from "invariant";
import { useSetAtom } from "jotai";
import {
useTransactionEventListener,
useTransactionEventListListener,
} from "utils";
import { EventData } from "types/events";
import { useTransactionEventListener } from "utils";

type TxWithAmount = { amount: BigNumber };

Expand All @@ -28,7 +26,7 @@ const getTotalAmountFromTransactionList = (txs: TxWithAmount[]): BigNumber =>
}, new BigNumber(0));

const parseTxsData = <T extends TxWithAmount>(
tx: TxProps,
tx: TxProps | TxProps[],
data: T[]
): { id: string; total: BigNumber } => {
const id = createNotificationId(tx);
Expand Down Expand Up @@ -349,72 +347,59 @@ export const useTransactionNotifications = (): void => {
});
});

useTransactionEventListListener(
[
"TransparentTransfer.Error",
"ShieldedTransfer.Error",
"ShieldingTransfer.Error",
"UnshieldingTransfer.Error",
],
(e) => {
const tx = e.detail.tx;
const data: TxWithAmount[] = e.detail.data[0].data;
const { id } = parseTxsData(tx, data);
clearPendingNotifications(id);
const storedTx = searchAllStoredTxByHash(tx.hash);
dispatchNotification({
id,
type: "error",
title: "Transfer transaction failed",
description:
storedTx ?
<>
Your transfer transaction of{" "}
<TokenCurrency
symbol={storedTx.asset.symbol}
amount={storedTx.displayAmount}
/>{" "}
to {shortenAddress(storedTx.destinationAddress, 8, 8)} has failed
</>
: "Your transfer transaction has failed",
details:
e.detail.error?.message && failureDetails(e.detail.error.message),
});
}
);
const onTransferError = (e: EventData<unknown>): void => {
const id = createNotificationId(e.detail.tx);
clearPendingNotifications(id);
const storedTx = searchAllStoredTxByHash(e.detail.tx[0].hash);
dispatchNotification({
id,
type: "error",
title: "Transfer transaction failed",
description:
storedTx ?
<>
Your transfer transaction of{" "}
<TokenCurrency
symbol={storedTx.asset.symbol}
amount={storedTx.displayAmount}
/>{" "}
to {shortenAddress(storedTx.destinationAddress, 8, 8)} has failed
</>
: "Your transfer transaction has failed",
details:
e.detail.error?.message && failureDetails(e.detail.error.message),
});
};
useTransactionEventListener("TransparentTransfer.Error", onTransferError);
useTransactionEventListener("ShieldedTransfer.Error", onTransferError);
useTransactionEventListener("ShieldingTransfer.Error", onTransferError);
useTransactionEventListener("UnshieldingTransfer.Error", onTransferError);

useTransactionEventListListener(
[
"TransparentTransfer.Success",
"ShieldedTransfer.Success",
"ShieldingTransfer.Success",
"UnshieldingTransfer.Success",
],
(e) => {
const tx = e.detail.tx;
const data: TxWithAmount[] = e.detail.data[0].data;
const { id } = parseTxsData(tx, data);
clearPendingNotifications(id);
const storedTx = searchAllStoredTxByHash(tx.hash);
dispatchNotification({
id,
title: "Transfer transaction succeeded",
description:
storedTx ?
<>
Your transfer transaction of{" "}
<TokenCurrency
symbol={storedTx.asset.symbol}
amount={storedTx.displayAmount}
/>{" "}
to {shortenAddress(storedTx.destinationAddress, 8, 8)} has
succeeded
</>
: "Your transfer transaction has succeeded",
type: "success",
});
}
);
const onTransferSuccess = (e: EventData<unknown>): void => {
const id = createNotificationId(e.detail.tx);
clearPendingNotifications(id);
const storedTx = searchAllStoredTxByHash(e.detail.tx[0].hash);
dispatchNotification({
id,
title: "Transfer transaction succeeded",
description:
storedTx ?
<>
Your transfer transaction of{" "}
<TokenCurrency
symbol={storedTx.asset.symbol}
amount={storedTx.displayAmount}
/>{" "}
to {shortenAddress(storedTx.destinationAddress, 8, 8)} has succeeded
</>
: "Your transfer transaction has succeeded",
type: "success",
});
};
useTransactionEventListener("TransparentTransfer.Success", onTransferSuccess);
useTransactionEventListener("ShieldedTransfer.Success", onTransferSuccess);
useTransactionEventListener("ShieldingTransfer.Success", onTransferSuccess);
useTransactionEventListener("UnshieldingTransfer.Success", onTransferSuccess);

useTransactionEventListener("IbcTransfer.Success", (e) => {
invariant(e.detail.hash, "Notification error: Invalid Tx hash");
Expand Down
Loading

0 comments on commit 41b7158

Please sign in to comment.