From 1a276bd657c0a860980153418f02398f0e2de752 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 7 Apr 2025 14:16:44 +1200 Subject: [PATCH 01/17] fix: starting point for historical txns --- .../src/App/Transactions/TransactionCard.tsx | 33 ++-- .../App/Transactions/TransactionHistory.tsx | 154 +++++++++++++----- .../Transactions/TransactionHistoryList.tsx | 17 +- .../namadillo/src/atoms/transactions/atoms.ts | 33 +++- .../src/atoms/transactions/services.ts | 16 +- 5 files changed, 185 insertions(+), 68 deletions(-) diff --git a/apps/namadillo/src/App/Transactions/TransactionCard.tsx b/apps/namadillo/src/App/Transactions/TransactionCard.tsx index 843bb9bf8d..ba9e7d0ce9 100644 --- a/apps/namadillo/src/App/Transactions/TransactionCard.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionCard.tsx @@ -1,22 +1,22 @@ +import { TransactionHistory } from "@namada/indexer-client"; import { TokenCurrency } from "App/Common/TokenCurrency"; import { routes } from "App/routes"; import { parseChainInfo } from "App/Transfer/common"; import { chainRegistryAtom } from "atoms/integrations"; import clsx from "clsx"; import { useAtomValue } from "jotai"; -import { GoIssueClosed, GoIssueTrackedBy, GoXCircle } from "react-icons/go"; +import { GoIssueClosed, GoXCircle } from "react-icons/go"; import { ImCheckmark } from "react-icons/im"; import { generatePath, useNavigate } from "react-router-dom"; import { twMerge } from "tailwind-merge"; import { ibcTransferStages, - IbcTransferTransactionData, namadaTransferStages, TransferTransactionData, } from "types"; type TransactionCardProps = { - transaction: TransferTransactionData; + transaction: TransactionHistory["tx"]; }; const getTitle = (transferTransaction: TransferTransactionData): string => { @@ -36,14 +36,15 @@ const getTitle = (transferTransaction: TransferTransactionData): string => { export const TransactionCard = ({ transaction, }: TransactionCardProps): JSX.Element => { + console.log(transaction, "txnnn"); const navigate = useNavigate(); const availableChains = useAtomValue(chainRegistryAtom); - const isIbc = Object.keys(ibcTransferStages).includes(transaction.type); + const isIbc = Object.keys(ibcTransferStages).includes( + transaction?.kind ?? "" + ); const chainId = - isIbc ? - (transaction as IbcTransferTransactionData).destinationChainId - : transaction.chainId; + isIbc ? transaction?.destinationChainId : transaction?.chainId; const chainName = chainId in availableChains ? @@ -61,34 +62,34 @@ export const TransactionCard = ({ ) )} onClick={() => - transaction.hash && - navigate(generatePath(routes.transaction, { hash: transaction.hash })) + transaction?.txId && + navigate(generatePath(routes.transaction, { hash: transaction.txId })) } > - {transaction.status === "success" && } - {transaction.status === "pending" && } - {transaction.status === "error" && } + {transaction?.exitCode === "applied" && } + {/* {transaction?.exitCode === "pending" && } */} + {transaction?.exitCode === "rejected" && }

{getTitle(transaction)}

{" "} to {chainName} {transaction.destinationAddress}

- {transaction.status === "success" && } + {transaction?.exitCode === "applied" && } ); diff --git a/apps/namadillo/src/App/Transactions/TransactionHistory.tsx b/apps/namadillo/src/App/Transactions/TransactionHistory.tsx index 7f10272ead..d44c6673a9 100644 --- a/apps/namadillo/src/App/Transactions/TransactionHistory.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionHistory.tsx @@ -1,55 +1,127 @@ -import { Panel } from "@namada/components"; +import { Panel, TableRow } from "@namada/components"; +import { TransactionHistory as TransactionHistoryType } from "@namada/indexer-client"; import { NavigationFooter } from "App/AccountOverview/NavigationFooter"; +import { PageLoader } from "App/Common/PageLoader"; +import { TableWithPaginator } from "App/Common/TableWithPaginator"; import { - completeTransactionsHistoryAtom, - myTransactionHistoryAtom, + chainTransactionHistoryFamily, pendingTransactionsHistoryAtom, } from "atoms/transactions/atoms"; -import { useTransactionActions } from "hooks/useTransactionActions"; import { useAtomValue } from "jotai"; -import { TransactionList } from "./TransactionHistoryList"; +import { useMemo, useState } from "react"; +import { twMerge } from "tailwind-merge"; +import { PendingTransactionCard } from "./PendingTransactionCard"; +import { TransactionCard } from "./TransactionCard"; + +const ITEMS_PER_PAGE = 30; +export const transferKindOptions = [ + "transparentTransfer", + "shieldingTransfer", + "unshieldingTransfer", + "shieldedTransfer", + "ibcTransparentTransfer", + "ibcShieldingTransfer", + "ibcUnshieldingTransfer", + "ibcShieldedTransfer", + "received", +]; export const TransactionHistory = (): JSX.Element => { - const transactions = useAtomValue(myTransactionHistoryAtom); + const [currentPage, setCurrentPage] = useState(0); const pending = useAtomValue(pendingTransactionsHistoryAtom); - const complete = useAtomValue(completeTransactionsHistoryAtom); - const hasNoTransactions = transactions.length === 0; - const { clearMyCompleteTransactions } = useTransactionActions(); + const { data: transactions, isLoading } = useAtomValue( + chainTransactionHistoryFamily({ perPage: ITEMS_PER_PAGE, fetchAll: true }) + ); + // Only show historical transactions that are in the transferKindOptions array + const historicalTransactions = + transactions?.results?.filter((transaction) => + transferKindOptions.includes(transaction.tx?.kind ?? "") + ) ?? []; + + // Calculate total pages based on the filtered transactions + const totalPages = Math.max( + 1, + Math.ceil(historicalTransactions.length / ITEMS_PER_PAGE) + ); + + // Create paginated data for the current page + const paginatedTransactions = useMemo(() => { + const startIndex = currentPage * ITEMS_PER_PAGE; + const endIndex = startIndex + ITEMS_PER_PAGE; + return historicalTransactions.slice(startIndex, endIndex); + }, [historicalTransactions, currentPage]); + + const renderRow = ( + transaction: TransactionHistoryType, + index: number + ): TableRow => { + return { + key: transaction.tx?.txId || index.toString(), + cells: [], + }; + }; + + const handlePageChange = (page: number): void => { + setCurrentPage(page); + }; return ( -
- -
-

Transfers made by this device

- {pending.length > 0 && ( -
-

In Progress

- -
- )} - {complete.length > 0 && ( -
-
-

History

- -
- -
- )} - {hasNoTransactions && ( -

No transactions saved on this device

- )} -
+
+ +

All Transfers made

+ + {pending.length > 0 && ( +
+

Pending

+
+ {pending.map((transaction) => ( + + ))} +
+
+ )} + + {isLoading ? + + :
+
+

History

+
+
+
+ tbody>tr:nth-child(odd)]:bg-transparent", + "[&>tbody>tr:nth-child(even)]:bg-transparent", + "w-full [&_td]:px-1 [&_th]:px-1 [&_td:first-child]:pl-4 [&_td]:h-[64px]", + "[&_td]:font-normal [&_td:last-child]:pr-4 [&_th:first-child]:pl-4 [&_th:last-child]:pr-4", + "[&_td:first-child]:rounded-s-md [&_td:last-child]:rounded-e-md" + ), + }} + /> +
+
+ {historicalTransactions.length === 0 && ( +

+ No transactions saved on this device +

+ )} +
+ }
- +
); }; diff --git a/apps/namadillo/src/App/Transactions/TransactionHistoryList.tsx b/apps/namadillo/src/App/Transactions/TransactionHistoryList.tsx index 63641c68f6..621a2b1db7 100644 --- a/apps/namadillo/src/App/Transactions/TransactionHistoryList.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionHistoryList.tsx @@ -1,16 +1,17 @@ -import { TransferTransactionData } from "types"; +import { TransactionHistory } from "@namada/indexer-client"; import { TransactionCard } from "./TransactionCard"; - export const TransactionList = ({ transactions, }: { - transactions: TransferTransactionData[]; + transactions: TransactionHistory[]; }): JSX.Element => (
    - {transactions.map((tx) => ( -
  • - -
  • - ))} + {transactions?.map(({ tx }) => { + return ( +
  • + +
  • + ); + })}
); diff --git a/apps/namadillo/src/atoms/transactions/atoms.ts b/apps/namadillo/src/atoms/transactions/atoms.ts index c5bcef3c56..ca62e24069 100644 --- a/apps/namadillo/src/atoms/transactions/atoms.ts +++ b/apps/namadillo/src/atoms/transactions/atoms.ts @@ -1,13 +1,15 @@ -import { defaultAccountAtom } from "atoms/accounts"; +import { Pagination, TransactionHistory } from "@namada/indexer-client"; +import { allDefaultAccountsAtom, defaultAccountAtom } from "atoms/accounts"; import { indexerApiAtom } from "atoms/api"; import { atom } from "jotai"; +import { atomWithQuery } from "jotai-tanstack-query"; import { atomWithStorage } from "jotai/utils"; import { Address, TransferTransactionData } from "types"; import { filterCompleteTransactions, filterPendingTransactions, } from "./functions"; -import { fetchTransaction } from "./services"; +import { fetchHistoricalTransactions, fetchTransaction } from "./services"; export const transactionStorageKey = "namadillo:transactions"; @@ -38,3 +40,30 @@ export const fetchTransactionAtom = atom((get) => { const api = get(indexerApiAtom); return (hash: string) => fetchTransaction(api, hash); }); + +export const chainTransactionHistoryAtom = atomWithQuery<{ + results: TransactionHistory[]; + pagination: Pagination; +}>((get) => { + const api = get(indexerApiAtom); + const accounts = get(allDefaultAccountsAtom); + const addresses = accounts.data?.map((acc) => acc.address); + return { + enabled: !!addresses, // Only run the query if we have addresses + queryKey: ["chain-transaction-history", addresses], + queryFn: async () => { + if (!addresses) { + return { + results: [], + pagination: { + totalPages: "0", + totalRecords: "0", + currentPage: "0", + recordsPerPage: "0", + }, + }; + } + return fetchHistoricalTransactions(api, addresses); + }, + }; +}); diff --git a/apps/namadillo/src/atoms/transactions/services.ts b/apps/namadillo/src/atoms/transactions/services.ts index 10d408fe98..c91871b339 100644 --- a/apps/namadillo/src/atoms/transactions/services.ts +++ b/apps/namadillo/src/atoms/transactions/services.ts @@ -1,5 +1,10 @@ import { IndexedTx, StargateClient } from "@cosmjs/stargate"; -import { DefaultApi, WrapperTransaction } from "@namada/indexer-client"; +import { + DefaultApi, + Pagination, + TransactionHistory, + WrapperTransaction, +} from "@namada/indexer-client"; import { IbcTransferTransactionData } from "types"; import { sanitizeAddress } from "utils/address"; @@ -67,3 +72,12 @@ export const fetchTransaction = async ( // indexer only accepts the hash as lowercase return (await api.apiV1ChainWrapperTxIdGet(sanitizeAddress(hash))).data; }; + +export const fetchHistoricalTransactions = async ( + api: DefaultApi, + addresses: string[] +): Promise<{ results: TransactionHistory[]; pagination: Pagination }> => { + const response = await api.apiV1ChainHistoryGet(addresses); + // Assuming the API response structure follows the same pattern as other endpoints + return response.data; +}; From c465570dcccefbe33a0903e1c7f0c82203747bef Mon Sep 17 00:00:00 2001 From: neocybereth Date: Tue, 29 Apr 2025 17:01:34 +1200 Subject: [PATCH 02/17] chore: update history fetching, list and cards --- .../src/App/Transactions/TransactionCard.tsx | 134 ++++++++++++------ .../namadillo/src/atoms/transactions/atoms.ts | 52 ++++++- .../src/atoms/transactions/services.ts | 16 ++- 3 files changed, 155 insertions(+), 47 deletions(-) diff --git a/apps/namadillo/src/App/Transactions/TransactionCard.tsx b/apps/namadillo/src/App/Transactions/TransactionCard.tsx index ba9e7d0ce9..433ba231a1 100644 --- a/apps/namadillo/src/App/Transactions/TransactionCard.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionCard.tsx @@ -1,55 +1,107 @@ import { TransactionHistory } from "@namada/indexer-client"; import { TokenCurrency } from "App/Common/TokenCurrency"; import { routes } from "App/routes"; -import { parseChainInfo } from "App/Transfer/common"; -import { chainRegistryAtom } from "atoms/integrations"; +import { chainAssetsMapAtom } from "atoms/chain"; +import BigNumber from "bignumber.js"; import clsx from "clsx"; import { useAtomValue } from "jotai"; import { GoIssueClosed, GoXCircle } from "react-icons/go"; import { ImCheckmark } from "react-icons/im"; import { generatePath, useNavigate } from "react-router-dom"; import { twMerge } from "tailwind-merge"; -import { - ibcTransferStages, - namadaTransferStages, - TransferTransactionData, -} from "types"; - -type TransactionCardProps = { - transaction: TransactionHistory["tx"]; +import { toDisplayAmount } from "utils"; + +type Tx = TransactionHistory["tx"]; +type Props = { transaction: Tx }; + +const IBC_PREFIX = "ibc"; + +const isIBCTransaction = (kind: string | undefined): boolean => { + if (!kind) return false; + return kind.startsWith("ibc"); }; -const getTitle = (transferTransaction: TransferTransactionData): string => { - const { type } = transferTransaction; +export function getToken(txn: Tx): string | undefined { + const parsed = txn?.data ? JSON.parse(txn.data) : undefined; + if (!parsed) return undefined; + const sections = Array.isArray(parsed) ? parsed : [parsed]; - if (Object.keys(namadaTransferStages).includes(type)) { - return "Transfer"; + // return the first token found in sources or targets + for (const section of sections) { + if (section.sources?.length) { + return section.sources[0].token; + } + if (section.targets?.length) { + return section.targets[0].token; + } } - if (Object.keys(ibcTransferStages).includes(type)) { - return "Transfer IBC"; - } + return undefined; +} - return ""; +const titleFor = (kind: string | undefined): string => { + if (!kind) return "Unknown"; + if (kind.startsWith(IBC_PREFIX)) return "Transfer IBC"; + if (kind === "transparenttransfer") return "Transparent Transfer"; + if (kind === "shieldingtransfer" || kind === "unshieldingtransfer") + return "Shielding Transfer"; + if (kind === "shieldedtransfer") return "Shielded Transfer"; + return "Transfer"; }; -export const TransactionCard = ({ - transaction, -}: TransactionCardProps): JSX.Element => { - console.log(transaction, "txnnn"); - const navigate = useNavigate(); - const availableChains = useAtomValue(chainRegistryAtom); - const isIbc = Object.keys(ibcTransferStages).includes( - transaction?.kind ?? "" - ); +type RawDataSection = { + amount?: string; + sources?: Array<{ amount: string; owner: string }>; + targets?: Array<{ amount: string; owner: string }>; +}; - const chainId = - isIbc ? transaction?.destinationChainId : transaction?.chainId; +export function getTransactionInfo( + tx: Tx +): { amount: BigNumber; receiver: string } | undefined { + let parsed: RawDataSection | RawDataSection[]; + if (typeof tx?.data === "string") { + parsed = JSON.parse(tx.data); + } else if (tx?.data) { + parsed = tx.data; + } else { + return undefined; + } - const chainName = - chainId in availableChains ? - parseChainInfo(availableChains[chainId].chain)?.pretty_name - : chainId; + const sections = Array.isArray(parsed) ? parsed : [parsed]; + if (sections.length === 0) return undefined; + + for (const sec of sections) { + // Try targets first (i.e. true transfers) + if (sec.targets?.length && sec.targets[0].amount && sec.targets[0].owner) { + return { + amount: new BigNumber(sec.targets[0].amount), + receiver: sec.targets[0].owner, + }; + } + // Fallback to sources + if (sec.sources?.length && sec.sources[0].amount && sec.sources[0].owner) { + return { + amount: new BigNumber(sec.sources[0].amount), + receiver: sec.sources[0].owner, + }; + } + } + + return undefined; +} + +export const TransactionCard = ({ transaction }: Props): JSX.Element => { + const navigate = useNavigate(); + const token = getToken(transaction); + const isIbc = isIBCTransaction(transaction?.kind); + const chainAssetsMap = useAtomValue(chainAssetsMapAtom); + const asset = token ? chainAssetsMap[token] : undefined; + const txnInfo = getTransactionInfo(transaction); + const baseAmount = + asset && txnInfo?.amount ? + toDisplayAmount(asset, txnInfo.amount) + : undefined; + const receiver = txnInfo?.receiver; return (
@@ -74,21 +125,22 @@ export const TransactionCard = ({ )} > {transaction?.exitCode === "applied" && } - {/* {transaction?.exitCode === "pending" && } */} {transaction?.exitCode === "rejected" && } +
-

{getTitle(transaction)}

+

{titleFor(transaction?.kind)}

{" "} - to {chainName} {transaction.destinationAddress} + to {receiver}

- + + {transaction?.exitCode === "applied" && }
diff --git a/apps/namadillo/src/atoms/transactions/atoms.ts b/apps/namadillo/src/atoms/transactions/atoms.ts index ca62e24069..3488f26f9d 100644 --- a/apps/namadillo/src/atoms/transactions/atoms.ts +++ b/apps/namadillo/src/atoms/transactions/atoms.ts @@ -3,7 +3,7 @@ import { allDefaultAccountsAtom, defaultAccountAtom } from "atoms/accounts"; import { indexerApiAtom } from "atoms/api"; import { atom } from "jotai"; import { atomWithQuery } from "jotai-tanstack-query"; -import { atomWithStorage } from "jotai/utils"; +import { atomFamily, atomWithStorage } from "jotai/utils"; import { Address, TransferTransactionData } from "types"; import { filterCompleteTransactions, @@ -41,6 +41,7 @@ export const fetchTransactionAtom = atom((get) => { return (hash: string) => fetchTransaction(api, hash); }); +// The original atom, kept for backward compatibility export const chainTransactionHistoryAtom = atomWithQuery<{ results: TransactionHistory[]; pagination: Pagination; @@ -57,9 +58,9 @@ export const chainTransactionHistoryAtom = atomWithQuery<{ results: [], pagination: { totalPages: "0", - totalRecords: "0", + totalItems: "0", currentPage: "0", - recordsPerPage: "0", + perPage: "0", }, }; } @@ -67,3 +68,48 @@ export const chainTransactionHistoryAtom = atomWithQuery<{ }, }; }); + +// New atom family for paginated transaction history +export const chainTransactionHistoryFamily = atomFamily( + (options?: { page?: number; perPage?: number }) => + atomWithQuery<{ + results: TransactionHistory[]; + pagination: Pagination; + }>((get) => { + const api = get(indexerApiAtom); + const accounts = get(allDefaultAccountsAtom); + const addresses = accounts.data?.map((acc) => acc.address); + + return { + enabled: !!addresses, // Only run the query if we have addresses + queryKey: [ + "chain-transaction-history", + addresses, + options?.page, + options?.perPage, + ], + queryFn: async () => { + if (!addresses) { + return { + results: [], + pagination: { + totalPages: "0", + totalItems: "0", + currentPage: "0", + perPage: "0", + }, + }; + } + return fetchHistoricalTransactions( + api, + addresses, + options?.page, + options?.perPage + ); + }, + }; + }), + (a, b) => + // Equality check for memoization + a?.page === b?.page && a?.perPage === b?.perPage +); diff --git a/apps/namadillo/src/atoms/transactions/services.ts b/apps/namadillo/src/atoms/transactions/services.ts index c91871b339..25db6610c0 100644 --- a/apps/namadillo/src/atoms/transactions/services.ts +++ b/apps/namadillo/src/atoms/transactions/services.ts @@ -75,9 +75,19 @@ export const fetchTransaction = async ( export const fetchHistoricalTransactions = async ( api: DefaultApi, - addresses: string[] + addresses: string[], + page?: number, + perPage?: number ): Promise<{ results: TransactionHistory[]; pagination: Pagination }> => { - const response = await api.apiV1ChainHistoryGet(addresses); - // Assuming the API response structure follows the same pattern as other endpoints + // indexer uses 1-based pagination + const pageParam = page !== undefined ? page + 1 : undefined; + + const response = await api.apiV1ChainHistoryGet(addresses, { + params: { + page: pageParam, + perPage: perPage, + }, + }); + return response.data; }; From 2ee1e76be8d958663e37c37c9f095c15f468be77 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Tue, 29 Apr 2025 17:08:14 +1200 Subject: [PATCH 03/17] fix: update received type --- apps/namadillo/src/App/Transactions/TransactionCard.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/namadillo/src/App/Transactions/TransactionCard.tsx b/apps/namadillo/src/App/Transactions/TransactionCard.tsx index 433ba231a1..5062ff32b5 100644 --- a/apps/namadillo/src/App/Transactions/TransactionCard.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionCard.tsx @@ -41,6 +41,7 @@ export function getToken(txn: Tx): string | undefined { const titleFor = (kind: string | undefined): string => { if (!kind) return "Unknown"; + if (kind === "received") return "Received"; if (kind.startsWith(IBC_PREFIX)) return "Transfer IBC"; if (kind === "transparenttransfer") return "Transparent Transfer"; if (kind === "shieldingtransfer" || kind === "unshieldingtransfer") From 40bb3c2c360bcbc87e1a2e68f2031f16ee7f4480 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Wed, 30 Apr 2025 10:05:25 +1200 Subject: [PATCH 04/17] fix: types --- .../src/App/Transactions/TransactionCard.tsx | 24 +++++++++++-------- .../Transactions/TransactionHistoryList.tsx | 17 ------------- .../src/atoms/transactions/services.ts | 2 +- 3 files changed, 15 insertions(+), 28 deletions(-) delete mode 100644 apps/namadillo/src/App/Transactions/TransactionHistoryList.tsx diff --git a/apps/namadillo/src/App/Transactions/TransactionCard.tsx b/apps/namadillo/src/App/Transactions/TransactionCard.tsx index 5062ff32b5..e19af1b37e 100644 --- a/apps/namadillo/src/App/Transactions/TransactionCard.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionCard.tsx @@ -1,4 +1,4 @@ -import { TransactionHistory } from "@namada/indexer-client"; +import { TransactionHistory as TransactionHistoryType } from "@namada/indexer-client"; import { TokenCurrency } from "App/Common/TokenCurrency"; import { routes } from "App/routes"; import { chainAssetsMapAtom } from "atoms/chain"; @@ -11,8 +11,8 @@ import { generatePath, useNavigate } from "react-router-dom"; import { twMerge } from "tailwind-merge"; import { toDisplayAmount } from "utils"; -type Tx = TransactionHistory["tx"]; -type Props = { transaction: Tx }; +type Tx = TransactionHistoryType; +type Props = { tx: Tx }; const IBC_PREFIX = "ibc"; @@ -21,7 +21,7 @@ const isIBCTransaction = (kind: string | undefined): boolean => { return kind.startsWith("ibc"); }; -export function getToken(txn: Tx): string | undefined { +export function getToken(txn: Tx["tx"]): string | undefined { const parsed = txn?.data ? JSON.parse(txn.data) : undefined; if (!parsed) return undefined; const sections = Array.isArray(parsed) ? parsed : [parsed]; @@ -39,9 +39,9 @@ export function getToken(txn: Tx): string | undefined { return undefined; } -const titleFor = (kind: string | undefined): string => { +const titleFor = (kind: string | undefined, isReceived: boolean): string => { if (!kind) return "Unknown"; - if (kind === "received") return "Received"; + if (isReceived) return "Received"; if (kind.startsWith(IBC_PREFIX)) return "Transfer IBC"; if (kind === "transparenttransfer") return "Transparent Transfer"; if (kind === "shieldingtransfer" || kind === "unshieldingtransfer") @@ -57,7 +57,7 @@ type RawDataSection = { }; export function getTransactionInfo( - tx: Tx + tx: Tx["tx"] ): { amount: BigNumber; receiver: string } | undefined { let parsed: RawDataSection | RawDataSection[]; if (typeof tx?.data === "string") { @@ -91,8 +91,11 @@ export function getTransactionInfo( return undefined; } -export const TransactionCard = ({ transaction }: Props): JSX.Element => { +export const TransactionCard = ({ tx }: Props): JSX.Element => { const navigate = useNavigate(); + const transactionTopLevel = tx; + const transaction = transactionTopLevel.tx; + const isReceived = transactionTopLevel?.kind === "received"; const token = getToken(transaction); const isIbc = isIBCTransaction(transaction?.kind); const chainAssetsMap = useAtomValue(chainAssetsMapAtom); @@ -108,7 +111,7 @@ export const TransactionCard = ({ transaction }: Props): JSX.Element => {
{
-

{titleFor(transaction?.kind)}

+

{titleFor(transaction?.kind, isReceived)}

{ {transaction?.exitCode === "applied" && } + {transaction?.exitCode === "rejected" && }

); diff --git a/apps/namadillo/src/App/Transactions/TransactionHistoryList.tsx b/apps/namadillo/src/App/Transactions/TransactionHistoryList.tsx deleted file mode 100644 index 621a2b1db7..0000000000 --- a/apps/namadillo/src/App/Transactions/TransactionHistoryList.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { TransactionHistory } from "@namada/indexer-client"; -import { TransactionCard } from "./TransactionCard"; -export const TransactionList = ({ - transactions, -}: { - transactions: TransactionHistory[]; -}): JSX.Element => ( -
    - {transactions?.map(({ tx }) => { - return ( -
  • - -
  • - ); - })} -
-); diff --git a/apps/namadillo/src/atoms/transactions/services.ts b/apps/namadillo/src/atoms/transactions/services.ts index 25db6610c0..d8e276fcc4 100644 --- a/apps/namadillo/src/atoms/transactions/services.ts +++ b/apps/namadillo/src/atoms/transactions/services.ts @@ -80,7 +80,7 @@ export const fetchHistoricalTransactions = async ( perPage?: number ): Promise<{ results: TransactionHistory[]; pagination: Pagination }> => { // indexer uses 1-based pagination - const pageParam = page !== undefined ? page + 1 : undefined; + const pageParam = page ? page : undefined; const response = await api.apiV1ChainHistoryGet(addresses, { params: { From bde504af811008333cc5120e92529916f5454a57 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Fri, 2 May 2025 11:30:39 +1200 Subject: [PATCH 05/17] fix: cleanup --- .../src/App/Transactions/TransactionCard.tsx | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/apps/namadillo/src/App/Transactions/TransactionCard.tsx b/apps/namadillo/src/App/Transactions/TransactionCard.tsx index e19af1b37e..47e92851de 100644 --- a/apps/namadillo/src/App/Transactions/TransactionCard.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionCard.tsx @@ -5,21 +5,24 @@ import { chainAssetsMapAtom } from "atoms/chain"; import BigNumber from "bignumber.js"; import clsx from "clsx"; import { useAtomValue } from "jotai"; -import { GoIssueClosed, GoXCircle } from "react-icons/go"; -import { ImCheckmark } from "react-icons/im"; +import { + IoArrowBack, + IoArrowForward, + IoCheckmarkCircleOutline, + IoCloseCircleOutline, +} from "react-icons/io5"; import { generatePath, useNavigate } from "react-router-dom"; import { twMerge } from "tailwind-merge"; import { toDisplayAmount } from "utils"; type Tx = TransactionHistoryType; type Props = { tx: Tx }; - -const IBC_PREFIX = "ibc"; - -const isIBCTransaction = (kind: string | undefined): boolean => { - if (!kind) return false; - return kind.startsWith("ibc"); +type RawDataSection = { + amount?: string; + sources?: Array<{ amount: string; owner: string }>; + targets?: Array<{ amount: string; owner: string }>; }; +const IBC_PREFIX = "ibc"; export function getToken(txn: Tx["tx"]): string | undefined { const parsed = txn?.data ? JSON.parse(txn.data) : undefined; @@ -50,12 +53,6 @@ const titleFor = (kind: string | undefined, isReceived: boolean): string => { return "Transfer"; }; -type RawDataSection = { - amount?: string; - sources?: Array<{ amount: string; owner: string }>; - targets?: Array<{ amount: string; owner: string }>; -}; - export function getTransactionInfo( tx: Tx["tx"] ): { amount: BigNumber; receiver: string } | undefined { @@ -97,7 +94,6 @@ export const TransactionCard = ({ tx }: Props): JSX.Element => { const transaction = transactionTopLevel.tx; const isReceived = transactionTopLevel?.kind === "received"; const token = getToken(transaction); - const isIbc = isIBCTransaction(transaction?.kind); const chainAssetsMap = useAtomValue(chainAssetsMapAtom); const asset = token ? chainAssetsMap[token] : undefined; const txnInfo = getTransactionInfo(transaction); @@ -121,15 +117,9 @@ export const TransactionCard = ({ tx }: Props): JSX.Element => { navigate(generatePath(routes.transaction, { hash: transaction.txId })) } > - - {transaction?.exitCode === "applied" && } - {transaction?.exitCode === "rejected" && } + + {isReceived && } + {!isReceived && }
@@ -144,9 +134,20 @@ export const TransactionCard = ({ tx }: Props): JSX.Element => {

- - {transaction?.exitCode === "applied" && } - {transaction?.exitCode === "rejected" && } + + {transaction?.exitCode === "applied" && ( + + )} + {transaction?.exitCode === "rejected" && ( + + )} ); From 74fe523eb461547a6d38b1fd8209882ef3220e83 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Fri, 2 May 2025 15:37:19 +1200 Subject: [PATCH 06/17] fix: remove clickability of card --- .../Transactions/PendingTransactionCard.tsx | 92 +++++++++++++++++++ .../src/App/Transactions/TransactionCard.tsx | 18 +--- .../namadillo/src/atoms/transactions/atoms.ts | 66 ++++++++++++- .../src/hooks/useTransactionActions.ts | 14 --- 4 files changed, 159 insertions(+), 31 deletions(-) create mode 100644 apps/namadillo/src/App/Transactions/PendingTransactionCard.tsx diff --git a/apps/namadillo/src/App/Transactions/PendingTransactionCard.tsx b/apps/namadillo/src/App/Transactions/PendingTransactionCard.tsx new file mode 100644 index 0000000000..a8d88d8c64 --- /dev/null +++ b/apps/namadillo/src/App/Transactions/PendingTransactionCard.tsx @@ -0,0 +1,92 @@ +import { TokenCurrency } from "App/Common/TokenCurrency"; +import { parseChainInfo } from "App/Transfer/common"; +import { chainRegistryAtom } from "atoms/integrations"; +import clsx from "clsx"; +import { useAtomValue } from "jotai"; +import { GoIssueClosed, GoIssueTrackedBy, GoXCircle } from "react-icons/go"; +import { ImCheckmark } from "react-icons/im"; +import { twMerge } from "tailwind-merge"; +import { + ibcTransferStages, + IbcTransferTransactionData, + namadaTransferStages, + TransferTransactionData, +} from "types"; + +type TransactionCardProps = { + transaction: TransferTransactionData; +}; + +const getTitle = (transferTransaction: TransferTransactionData): string => { + const { type } = transferTransaction; + + if (Object.keys(namadaTransferStages).includes(type)) { + return "Transfer"; + } + + if (Object.keys(ibcTransferStages).includes(type)) { + return "Transfer IBC"; + } + + return ""; +}; + +export const PendingTransactionCard = ({ + transaction, +}: TransactionCardProps): JSX.Element => { + const availableChains = useAtomValue(chainRegistryAtom); + const isIbc = Object.keys(ibcTransferStages).includes(transaction.type); + + const chainId = + isIbc ? + (transaction as IbcTransferTransactionData).destinationChainId + : transaction.chainId; + + const chainName = + chainId in availableChains ? + parseChainInfo(availableChains[chainId].chain)?.pretty_name + : chainId; + + return ( +
    +
  • +
    + + {transaction.status === "success" && } + {transaction.status === "pending" && } + {transaction.status === "error" && } + +
    +

    {getTitle(transaction)}

    +

    + {" "} + to {chainName} {transaction.destinationAddress} +

    +
    + + {transaction.status === "success" && } + +
    +
  • +
+ ); +}; diff --git a/apps/namadillo/src/App/Transactions/TransactionCard.tsx b/apps/namadillo/src/App/Transactions/TransactionCard.tsx index 47e92851de..952f3384a7 100644 --- a/apps/namadillo/src/App/Transactions/TransactionCard.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionCard.tsx @@ -1,6 +1,5 @@ import { TransactionHistory as TransactionHistoryType } from "@namada/indexer-client"; import { TokenCurrency } from "App/Common/TokenCurrency"; -import { routes } from "App/routes"; import { chainAssetsMapAtom } from "atoms/chain"; import BigNumber from "bignumber.js"; import clsx from "clsx"; @@ -11,7 +10,6 @@ import { IoCheckmarkCircleOutline, IoCloseCircleOutline, } from "react-icons/io5"; -import { generatePath, useNavigate } from "react-router-dom"; import { twMerge } from "tailwind-merge"; import { toDisplayAmount } from "utils"; @@ -45,11 +43,11 @@ export function getToken(txn: Tx["tx"]): string | undefined { const titleFor = (kind: string | undefined, isReceived: boolean): string => { if (!kind) return "Unknown"; if (isReceived) return "Received"; - if (kind.startsWith(IBC_PREFIX)) return "Transfer IBC"; - if (kind === "transparenttransfer") return "Transparent Transfer"; - if (kind === "shieldingtransfer" || kind === "unshieldingtransfer") + if (kind.startsWith(IBC_PREFIX)) return "IBC Transfer"; + if (kind === "transparentTransfer") return "Transparent Transfer"; + if (kind === "shieldingTransfer" || kind === "unshieldingTransfer") return "Shielding Transfer"; - if (kind === "shieldedtransfer") return "Shielded Transfer"; + if (kind === "shieldedTransfer") return "Shielded Transfer"; return "Transfer"; }; @@ -84,12 +82,10 @@ export function getTransactionInfo( }; } } - return undefined; } export const TransactionCard = ({ tx }: Props): JSX.Element => { - const navigate = useNavigate(); const transactionTopLevel = tx; const transaction = transactionTopLevel.tx; const isReceived = transactionTopLevel?.kind === "received"; @@ -107,15 +103,11 @@ export const TransactionCard = ({ tx }: Props): JSX.Element => {
- transaction?.txId && - navigate(generatePath(routes.transaction, { hash: transaction.txId })) - } > {isReceived && } diff --git a/apps/namadillo/src/atoms/transactions/atoms.ts b/apps/namadillo/src/atoms/transactions/atoms.ts index 3488f26f9d..38d0fd30cf 100644 --- a/apps/namadillo/src/atoms/transactions/atoms.ts +++ b/apps/namadillo/src/atoms/transactions/atoms.ts @@ -71,7 +71,7 @@ export const chainTransactionHistoryAtom = atomWithQuery<{ // New atom family for paginated transaction history export const chainTransactionHistoryFamily = atomFamily( - (options?: { page?: number; perPage?: number }) => + (options?: { page?: number; perPage?: number; fetchAll?: boolean }) => atomWithQuery<{ results: TransactionHistory[]; pagination: Pagination; @@ -85,7 +85,7 @@ export const chainTransactionHistoryFamily = atomFamily( queryKey: [ "chain-transaction-history", addresses, - options?.page, + options?.fetchAll ? "all" : options?.page, options?.perPage, ], queryFn: async () => { @@ -100,6 +100,62 @@ export const chainTransactionHistoryFamily = atomFamily( }, }; } + + // If fetchAll is true, we'll get all pages + if (options?.fetchAll) { + // First fetch to get pagination info + const firstPageResult = await fetchHistoricalTransactions( + api, + addresses, + 1, + options?.perPage || 10 + ); + + const totalPages = parseInt( + firstPageResult.pagination?.totalPages || "0" + ); + + // If there's only one page, return the first result + if (totalPages <= 1) { + return firstPageResult; + } + + // Otherwise, fetch all remaining pages + const allPagePromises = []; + // We already have page 1 + const allResults = [...firstPageResult.results]; + + // Fetch pages 2 to totalPages + for (let page = 2; page <= totalPages; page++) { + allPagePromises.push( + fetchHistoricalTransactions( + api, + addresses, + page, + options?.perPage || 10 + ) + ); + } + + const allPagesResults = await Promise.all(allPagePromises); + + // Combine all results + for (const pageResult of allPagesResults) { + allResults.push(...pageResult.results); + } + + // Return combined results with updated pagination info + return { + results: allResults, + pagination: { + ...firstPageResult.pagination, + totalPages: totalPages.toString(), + currentPage: "all", // Indicate we fetched all pages + }, + }; + } + + // Standard case: fetch a single page return fetchHistoricalTransactions( api, addresses, @@ -110,6 +166,8 @@ export const chainTransactionHistoryFamily = atomFamily( }; }), (a, b) => - // Equality check for memoization - a?.page === b?.page && a?.perPage === b?.perPage + // Equality check for memoization - include fetchAll in comparison + a?.page === b?.page && + a?.perPage === b?.perPage && + a?.fetchAll === b?.fetchAll ); diff --git a/apps/namadillo/src/hooks/useTransactionActions.ts b/apps/namadillo/src/hooks/useTransactionActions.ts index 86c08b823e..8a43f10d2f 100644 --- a/apps/namadillo/src/hooks/useTransactionActions.ts +++ b/apps/namadillo/src/hooks/useTransactionActions.ts @@ -10,7 +10,6 @@ type UseTransactionActionsOutput = { transactions: TransferTransactionData[]; findByHash: (hash: string) => TransferTransactionData | undefined; storeTransaction: (tx: TransferTransactionData) => void; - clearMyCompleteTransactions: () => void; changeTransaction: ( hash: string, updatedTx: Partial, @@ -61,23 +60,10 @@ export const useTransactionActions = (): UseTransactionActionsOutput => { return transactions.find((t) => t.hash === hash); }; - const clearMyCompleteTransactions = (): void => { - if (!account) return; - setTransactions((txs) => { - return { - ...txs, - [account.address]: txs[account.address].filter( - (tx) => tx.status === "pending" || tx.status === "idle" - ), - }; - }); - }; - return { transactions, findByHash, storeTransaction, changeTransaction, - clearMyCompleteTransactions, }; }; From b5adafc0b258236866f231308808008bdbb9f2aa Mon Sep 17 00:00:00 2001 From: neocybereth Date: Tue, 6 May 2025 16:57:23 +1200 Subject: [PATCH 07/17] chore: test txn --- .../src/App/Common/TransactionReceipt.tsx | 45 +++++++++++++++++++ .../App/Transactions/TransactionDetails.tsx | 38 ++++++++++++++-- .../App/Transactions/TransactionHistory.tsx | 5 +++ 3 files changed, 85 insertions(+), 3 deletions(-) diff --git a/apps/namadillo/src/App/Common/TransactionReceipt.tsx b/apps/namadillo/src/App/Common/TransactionReceipt.tsx index ea1ee97307..3668c6f6e4 100644 --- a/apps/namadillo/src/App/Common/TransactionReceipt.tsx +++ b/apps/namadillo/src/App/Common/TransactionReceipt.tsx @@ -134,6 +134,51 @@ const TransferTransactionReceipt = ({ ); }; +const ibcTransparentTransfer = { + tx: { + txId: "20979de7445767359b91ce609dae3ea45e94f34aaca30ab03fbb1382c0bd6802", + wrapperId: + "7cf3bf2e0078056bc97fdbb3922603e65be9fb5da61fdf9ef642f404ea21a6f7", + kind: "ibcTransparentTransfer", + data: '[{"Ibc":{"address":{"Account":"tnam1p5z8ruwyu7ha8urhq2l0dhpk2f5dv3ts7uyf2n75"},"trace":{"IbcTrace":"transfer/channel-1/uosmo"}}},{"sources":[{"owner":"tnam1qcqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqvtr7x4","token":"tnam1p5z8ruwyu7ha8urhq2l0dhpk2f5dv3ts7uyf2n75","amount":"1000000"}],"targets":[{"amount":"1000000","token":"tnam1p5z8ruwyu7ha8urhq2l0dhpk2f5dv3ts7uyf2n75","owner":"tnam1qzuq58crq9sv35fa79u7a82fy99plk3gpve30cxs"}],"shielded_section_hash":null}]', + memo: "52656c6179656420627920616e6f64656f667a656e21", + exitCode: "applied", + }, + target: "tnam1qzuq58crq9sv35fa79u7a82fy99plk3gpve30cxs", + kind: "received", + blockHeight: 1209630, +}; + +const sent = { + tx: { + txId: "2b2dd22a6dcd6541bf4ab8f8e36761e507da577ddf9ab070dbaa441bdc8db26a", + wrapperId: + "f527e7eeaa768d6bb9654d002059aad9cdbc9f053b6b4273695e4590bf5941ca", + kind: "shieldingTransfer", + data: '{"sources":[{"amount":"4180900","token":"tnam1p5z8ruwyu7ha8urhq2l0dhpk2f5dv3ts7uyf2n75","owner":"tnam1qzuq58crq9sv35fa79u7a82fy99plk3gpve30cxs"}],"targets":[{"owner":"tnam1pcqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzmefah","token":"tnam1p5z8ruwyu7ha8urhq2l0dhpk2f5dv3ts7uyf2n75","amount":"4180900"}],"shielded_section_hash":[186,197,223,158,116,227,210,39,36,13,236,55,84,160,48,140,12,221,65,228,44,15,240,155,183,234,68,65,214,18,248,56]}', + memo: null, + exitCode: "applied", + }, + target: "tnam1qzuq58crq9sv35fa79u7a82fy99plk3gpve30cxs", + kind: "sent", + blockHeight: 1672972, +}; + +const receieved = { + tx: { + txId: "c0c9d0815d3c0f6611b7b7f65595f5550bb9ea61cd563ade9776a7f25f5d9c28", + wrapperId: + "ccc28eea4dcc502fc3429dc7054562bfeb4ea0dd771a74d32b89c23625ff9741", + kind: "ibcTransparentTransfer", + data: '[{"Ibc":{"address":{"Account":"tnam1p5z8ruwyu7ha8urhq2l0dhpk2f5dv3ts7uyf2n75"},"trace":{"IbcTrace":"transfer/channel-1/uosmo"}}},{"sources":[{"owner":"tnam1qcqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqvtr7x4","token":"tnam1p5z8ruwyu7ha8urhq2l0dhpk2f5dv3ts7uyf2n75","amount":"1000"}],"targets":[{"token":"tnam1p5z8ruwyu7ha8urhq2l0dhpk2f5dv3ts7uyf2n75","owner":"tnam1qzuq58crq9sv35fa79u7a82fy99plk3gpve30cxs","amount":"1000"}],"shielded_section_hash":null}]', + memo: "2b2052656c6179656420627920436f736d69632056616c696461746f72", + exitCode: "applied", + }, + target: "tnam1qzuq58crq9sv35fa79u7a82fy99plk3gpve30cxs", + kind: "received", + blockHeight: 1209608, +}; + export const TransactionReceipt = ({ transaction, }: TransactionReceiptProps): JSX.Element => { diff --git a/apps/namadillo/src/App/Transactions/TransactionDetails.tsx b/apps/namadillo/src/App/Transactions/TransactionDetails.tsx index 85ea49c36c..7e9420addb 100644 --- a/apps/namadillo/src/App/Transactions/TransactionDetails.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionDetails.tsx @@ -1,14 +1,46 @@ import { Panel } from "@namada/components"; import { useSanitizedParams } from "@namada/hooks"; +import { WrapperTransaction } from "@namada/indexer-client"; import { TransactionReceipt } from "App/Common/TransactionReceipt"; -import { useTransactionActions } from "hooks/useTransactionActions"; +import { indexerApiAtom } from "atoms/api"; +import { useAtomValue } from "jotai"; +import { useEffect, useState } from "react"; import { TransactionNotFoundPanel } from "./TransactionNotFoundPanel"; export const TransactionDetails = (): JSX.Element => { const { hash } = useSanitizedParams(); - const { findByHash } = useTransactionActions(); + const api = useAtomValue(indexerApiAtom); + const [transaction, setTransaction] = useState( + null + ); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const fetchTransactionData = async (): Promise => { + try { + if (hash) { + const response = await api.apiV1ChainWrapperTxIdGet(hash); + setTransaction(response.data); + } + } catch (error) { + console.error("Error fetching transaction:", error); + } finally { + setLoading(false); + } + }; + + fetchTransactionData(); + }, [hash, api]); + + if (loading) { + return ( + +

Transactions

+
Loading transaction details...
+
+ ); + } - const transaction = findByHash(hash || ""); if (!transaction) { return ; } diff --git a/apps/namadillo/src/App/Transactions/TransactionHistory.tsx b/apps/namadillo/src/App/Transactions/TransactionHistory.tsx index d44c6673a9..92df0f9c29 100644 --- a/apps/namadillo/src/App/Transactions/TransactionHistory.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionHistory.tsx @@ -26,6 +26,9 @@ export const transferKindOptions = [ "received", ]; +// NOTES: +// - WrapperID is the transaction hash +// - TxID is the inner transaction export const TransactionHistory = (): JSX.Element => { const [currentPage, setCurrentPage] = useState(0); const pending = useAtomValue(pendingTransactionsHistoryAtom); @@ -51,6 +54,8 @@ export const TransactionHistory = (): JSX.Element => { return historicalTransactions.slice(startIndex, endIndex); }, [historicalTransactions, currentPage]); + console.log(paginatedTransactions); + const renderRow = ( transaction: TransactionHistoryType, index: number From 44b46cb0d5c4033e89aca9393fac841e45c5fa13 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Thu, 8 May 2025 20:50:14 +1200 Subject: [PATCH 08/17] fix: update types --- apps/namadillo/src/App/Common/TransactionReceipt.tsx | 9 +++------ .../src/App/Transactions/TransactionDetails.tsx | 8 +++----- .../src/App/Transactions/TransactionHistory.tsx | 2 +- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/apps/namadillo/src/App/Common/TransactionReceipt.tsx b/apps/namadillo/src/App/Common/TransactionReceipt.tsx index 3668c6f6e4..af4d3eadc3 100644 --- a/apps/namadillo/src/App/Common/TransactionReceipt.tsx +++ b/apps/namadillo/src/App/Common/TransactionReceipt.tsx @@ -1,5 +1,6 @@ import { Chain } from "@chain-registry/types"; import { CopyToClipboardControl, Stack } from "@namada/components"; +import { WrapperTransactionInnerTransactionsInner } from "@namada/indexer-client"; import { shortenAddress } from "@namada/utils"; import { isNamadaAddress, @@ -17,14 +18,10 @@ import { wallets } from "integrations"; import { useMemo } from "react"; import { FaCheckCircle } from "react-icons/fa"; import { GoHourglass, GoXCircle } from "react-icons/go"; -import { - allTransferTypes, - PartialTransferTransactionData, - TransferStep, -} from "types"; +import { allTransferTypes, TransferStep } from "types"; type TransactionReceiptProps = { - transaction: PartialTransferTransactionData; + transaction: WrapperTransactionInnerTransactionsInner; }; const stepDescription: Record = { diff --git a/apps/namadillo/src/App/Transactions/TransactionDetails.tsx b/apps/namadillo/src/App/Transactions/TransactionDetails.tsx index 7e9420addb..267fd2c884 100644 --- a/apps/namadillo/src/App/Transactions/TransactionDetails.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionDetails.tsx @@ -1,6 +1,6 @@ import { Panel } from "@namada/components"; import { useSanitizedParams } from "@namada/hooks"; -import { WrapperTransaction } from "@namada/indexer-client"; +import { InnerTransaction } from "@namada/indexer-client"; import { TransactionReceipt } from "App/Common/TransactionReceipt"; import { indexerApiAtom } from "atoms/api"; import { useAtomValue } from "jotai"; @@ -10,16 +10,14 @@ import { TransactionNotFoundPanel } from "./TransactionNotFoundPanel"; export const TransactionDetails = (): JSX.Element => { const { hash } = useSanitizedParams(); const api = useAtomValue(indexerApiAtom); - const [transaction, setTransaction] = useState( - null - ); + const [transaction, setTransaction] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { const fetchTransactionData = async (): Promise => { try { if (hash) { - const response = await api.apiV1ChainWrapperTxIdGet(hash); + const response = await api.apiV1ChainInnerTxIdGet(hash); setTransaction(response.data); } } catch (error) { diff --git a/apps/namadillo/src/App/Transactions/TransactionHistory.tsx b/apps/namadillo/src/App/Transactions/TransactionHistory.tsx index 92df0f9c29..c46ac9260e 100644 --- a/apps/namadillo/src/App/Transactions/TransactionHistory.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionHistory.tsx @@ -54,7 +54,7 @@ export const TransactionHistory = (): JSX.Element => { return historicalTransactions.slice(startIndex, endIndex); }, [historicalTransactions, currentPage]); - console.log(paginatedTransactions); + console.log(historicalTransactions); const renderRow = ( transaction: TransactionHistoryType, From c3955bfdd9a558f081e3e593f071a162e77fdf78 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 12 May 2025 12:39:10 +1200 Subject: [PATCH 09/17] chore: update to new design views --- apps/namadillo/src/App/AppRoutes.tsx | 5 - .../src/App/Common/TransactionReceipt.tsx | 232 ------------------ .../src/App/Transactions/TransactionCard.tsx | 206 +++++++++++----- .../App/Transactions/TransactionDetails.tsx | 52 ---- .../App/Transactions/TransactionHistory.tsx | 2 +- .../Transactions/TransactionNotFoundPanel.tsx | 21 -- apps/namadillo/src/atoms/balance/services.ts | 8 + 7 files changed, 159 insertions(+), 367 deletions(-) delete mode 100644 apps/namadillo/src/App/Common/TransactionReceipt.tsx delete mode 100644 apps/namadillo/src/App/Transactions/TransactionDetails.tsx delete mode 100644 apps/namadillo/src/App/Transactions/TransactionNotFoundPanel.tsx diff --git a/apps/namadillo/src/App/AppRoutes.tsx b/apps/namadillo/src/App/AppRoutes.tsx index 0237345644..3fd2ec046c 100644 --- a/apps/namadillo/src/App/AppRoutes.tsx +++ b/apps/namadillo/src/App/AppRoutes.tsx @@ -45,7 +45,6 @@ import { StakingRewards } from "./Staking/StakingRewards"; import { StakingWithdrawModal } from "./Staking/StakingWithdrawModal"; import { Unstake } from "./Staking/Unstake"; import { SwitchAccountPanel } from "./SwitchAccount/SwitchAccountPanel"; -import { TransactionDetails } from "./Transactions/TransactionDetails"; import { TransactionHistory } from "./Transactions/TransactionHistory"; import { TransferLayout } from "./Transfer/TransferLayout"; @@ -126,10 +125,6 @@ export const MainRoutes = (): JSX.Element => { {(features.namTransfersEnabled || features.ibcTransfersEnabled) && ( } /> - } - /> )} diff --git a/apps/namadillo/src/App/Common/TransactionReceipt.tsx b/apps/namadillo/src/App/Common/TransactionReceipt.tsx deleted file mode 100644 index af4d3eadc3..0000000000 --- a/apps/namadillo/src/App/Common/TransactionReceipt.tsx +++ /dev/null @@ -1,232 +0,0 @@ -import { Chain } from "@chain-registry/types"; -import { CopyToClipboardControl, Stack } from "@namada/components"; -import { WrapperTransactionInnerTransactionsInner } from "@namada/indexer-client"; -import { shortenAddress } from "@namada/utils"; -import { - isNamadaAddress, - isShieldedAddress, - parseChainInfo, -} from "App/Transfer/common"; -import { SelectedChain } from "App/Transfer/SelectedChain"; -import { SelectedWallet } from "App/Transfer/SelectedWallet"; -import { TokenAmountCard } from "App/Transfer/TokenAmountCard"; -import { TransferArrow } from "App/Transfer/TransferArrow"; -import { findChainById } from "atoms/integrations"; -import BigNumber from "bignumber.js"; -import clsx from "clsx"; -import { wallets } from "integrations"; -import { useMemo } from "react"; -import { FaCheckCircle } from "react-icons/fa"; -import { GoHourglass, GoXCircle } from "react-icons/go"; -import { allTransferTypes, TransferStep } from "types"; - -type TransactionReceiptProps = { - transaction: WrapperTransactionInnerTransactionsInner; -}; - -const stepDescription: Record = { - sign: "Signature required", - "ibc-to-shielded": "IBC Transfer to Namada MASP", - "zk-proof": "Generating ZK Proof", - "ibc-to-transparent": "IBC Transfer to Namada", - "shielded-to-shielded": "Transfer to Namada Shielded", - "transparent-to-shielded": "Transfer to Namada Shielded", - "transparent-to-transparent": "Transfer to Namada Transparent", - "shielded-to-transparent": "Transfer to Namada Transparent", - "ibc-withdraw": "Transfer from Namada", - "waiting-confirmation": "Waiting for confirmation", - complete: "Transfer Complete", -}; - -const TransferTransactionReceipt = ({ - transaction, -}: TransactionReceiptProps): JSX.Element => { - const getChain = (chainId: string, address: string): Chain | undefined => { - const chain = findChainById(chainId); - if (isNamadaAddress(address) && chain) { - return parseChainInfo(chain, isShieldedAddress(address || "")); - } - return chain; - }; - - const sourceChain = useMemo(() => { - return getChain(transaction.chainId, transaction.sourceAddress || ""); - }, [transaction]); - - const destinationChain = useMemo(() => { - return getChain( - "destinationChainId" in transaction ? - transaction.destinationChainId || "" - : transaction.chainId, - transaction.destinationAddress || "" - ); - }, [transaction]); - - const sourceWallet = - isNamadaAddress(transaction.sourceAddress || "") ? - wallets.namada - : wallets.keplr; - - const destinationWallet = - isNamadaAddress(transaction.destinationAddress || "") ? - wallets.namada - : wallets.keplr; - - return ( - -
-
- {sourceChain && ( - - )} - {sourceWallet && ( - - )} -
-
- -
- - - -
- -
-
-
- {destinationChain && ( - - )} - {destinationWallet && ( - - )} -
-
-
-
- ); -}; - -const ibcTransparentTransfer = { - tx: { - txId: "20979de7445767359b91ce609dae3ea45e94f34aaca30ab03fbb1382c0bd6802", - wrapperId: - "7cf3bf2e0078056bc97fdbb3922603e65be9fb5da61fdf9ef642f404ea21a6f7", - kind: "ibcTransparentTransfer", - data: '[{"Ibc":{"address":{"Account":"tnam1p5z8ruwyu7ha8urhq2l0dhpk2f5dv3ts7uyf2n75"},"trace":{"IbcTrace":"transfer/channel-1/uosmo"}}},{"sources":[{"owner":"tnam1qcqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqvtr7x4","token":"tnam1p5z8ruwyu7ha8urhq2l0dhpk2f5dv3ts7uyf2n75","amount":"1000000"}],"targets":[{"amount":"1000000","token":"tnam1p5z8ruwyu7ha8urhq2l0dhpk2f5dv3ts7uyf2n75","owner":"tnam1qzuq58crq9sv35fa79u7a82fy99plk3gpve30cxs"}],"shielded_section_hash":null}]', - memo: "52656c6179656420627920616e6f64656f667a656e21", - exitCode: "applied", - }, - target: "tnam1qzuq58crq9sv35fa79u7a82fy99plk3gpve30cxs", - kind: "received", - blockHeight: 1209630, -}; - -const sent = { - tx: { - txId: "2b2dd22a6dcd6541bf4ab8f8e36761e507da577ddf9ab070dbaa441bdc8db26a", - wrapperId: - "f527e7eeaa768d6bb9654d002059aad9cdbc9f053b6b4273695e4590bf5941ca", - kind: "shieldingTransfer", - data: '{"sources":[{"amount":"4180900","token":"tnam1p5z8ruwyu7ha8urhq2l0dhpk2f5dv3ts7uyf2n75","owner":"tnam1qzuq58crq9sv35fa79u7a82fy99plk3gpve30cxs"}],"targets":[{"owner":"tnam1pcqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzmefah","token":"tnam1p5z8ruwyu7ha8urhq2l0dhpk2f5dv3ts7uyf2n75","amount":"4180900"}],"shielded_section_hash":[186,197,223,158,116,227,210,39,36,13,236,55,84,160,48,140,12,221,65,228,44,15,240,155,183,234,68,65,214,18,248,56]}', - memo: null, - exitCode: "applied", - }, - target: "tnam1qzuq58crq9sv35fa79u7a82fy99plk3gpve30cxs", - kind: "sent", - blockHeight: 1672972, -}; - -const receieved = { - tx: { - txId: "c0c9d0815d3c0f6611b7b7f65595f5550bb9ea61cd563ade9776a7f25f5d9c28", - wrapperId: - "ccc28eea4dcc502fc3429dc7054562bfeb4ea0dd771a74d32b89c23625ff9741", - kind: "ibcTransparentTransfer", - data: '[{"Ibc":{"address":{"Account":"tnam1p5z8ruwyu7ha8urhq2l0dhpk2f5dv3ts7uyf2n75"},"trace":{"IbcTrace":"transfer/channel-1/uosmo"}}},{"sources":[{"owner":"tnam1qcqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqvtr7x4","token":"tnam1p5z8ruwyu7ha8urhq2l0dhpk2f5dv3ts7uyf2n75","amount":"1000"}],"targets":[{"token":"tnam1p5z8ruwyu7ha8urhq2l0dhpk2f5dv3ts7uyf2n75","owner":"tnam1qzuq58crq9sv35fa79u7a82fy99plk3gpve30cxs","amount":"1000"}],"shielded_section_hash":null}]', - memo: "2b2052656c6179656420627920436f736d69632056616c696461746f72", - exitCode: "applied", - }, - target: "tnam1qzuq58crq9sv35fa79u7a82fy99plk3gpve30cxs", - kind: "received", - blockHeight: 1209608, -}; - -export const TransactionReceipt = ({ - transaction, -}: TransactionReceiptProps): JSX.Element => { - const isTransferTransaction = (): boolean => { - return allTransferTypes.includes(transaction.type); - }; - - return ( -
-
-
- {transaction.status === "error" && ( - {} - )} - {transaction.status === "success" && ( - {} - )} - {transaction.status === "pending" && ( - {} - )} -
-

- {transaction.status === "error" ? - "Transaction Failed" - : stepDescription[transaction.currentStep || "sign"]} -

- {transaction.errorMessage && ( -
- {transaction.errorMessage} -
- )} - {transaction.hash && ( - - Transaction hash:{" "} - - {shortenAddress(transaction.hash, 8, 8)} - - - - )} -
-
- {isTransferTransaction() ? - - :
} -
-
- ); -}; diff --git a/apps/namadillo/src/App/Transactions/TransactionCard.tsx b/apps/namadillo/src/App/Transactions/TransactionCard.tsx index 952f3384a7..70c098a6f1 100644 --- a/apps/namadillo/src/App/Transactions/TransactionCard.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionCard.tsx @@ -1,9 +1,16 @@ import { TransactionHistory as TransactionHistoryType } from "@namada/indexer-client"; +import { shortenAddress } from "@namada/utils"; import { TokenCurrency } from "App/Common/TokenCurrency"; +import { AssetImage } from "App/Transfer/AssetImage"; +import { isShieldedAddress, isTransparentAddress } from "App/Transfer/common"; +import { indexerApiAtom } from "atoms/api"; +import { fetchBlockByHeight } from "atoms/balance/services"; import { chainAssetsMapAtom } from "atoms/chain"; import BigNumber from "bignumber.js"; import clsx from "clsx"; import { useAtomValue } from "jotai"; +import { useEffect, useState } from "react"; +import { FaLock } from "react-icons/fa6"; import { IoArrowBack, IoArrowForward, @@ -12,6 +19,7 @@ import { } from "react-icons/io5"; import { twMerge } from "tailwind-merge"; import { toDisplayAmount } from "utils"; +import keplrSvg from "../../integrations/assets/keplr.svg"; type Tx = TransactionHistoryType; type Props = { tx: Tx }; @@ -42,7 +50,7 @@ export function getToken(txn: Tx["tx"]): string | undefined { const titleFor = (kind: string | undefined, isReceived: boolean): string => { if (!kind) return "Unknown"; - if (isReceived) return "Received"; + if (isReceived) return "Receive"; if (kind.startsWith(IBC_PREFIX)) return "IBC Transfer"; if (kind === "transparentTransfer") return "Transparent Transfer"; if (kind === "shieldingTransfer" || kind === "unshieldingTransfer") @@ -53,36 +61,31 @@ const titleFor = (kind: string | undefined, isReceived: boolean): string => { export function getTransactionInfo( tx: Tx["tx"] -): { amount: BigNumber; receiver: string } | undefined { - let parsed: RawDataSection | RawDataSection[]; - if (typeof tx?.data === "string") { - parsed = JSON.parse(tx.data); - } else if (tx?.data) { - parsed = tx.data; - } else { - return undefined; - } +): { amount: BigNumber; sender?: string; receiver?: string } | undefined { + if (!tx?.data) return undefined; - const sections = Array.isArray(parsed) ? parsed : [parsed]; - if (sections.length === 0) return undefined; + const parsed = typeof tx.data === "string" ? JSON.parse(tx.data) : tx.data; + const sections: RawDataSection[] = Array.isArray(parsed) ? parsed : [parsed]; + + let sender: string | undefined; + let receiver: string | undefined; + let amount: BigNumber | undefined; for (const sec of sections) { - // Try targets first (i.e. true transfers) - if (sec.targets?.length && sec.targets[0].amount && sec.targets[0].owner) { - return { - amount: new BigNumber(sec.targets[0].amount), - receiver: sec.targets[0].owner, - }; + if (!amount && sec.targets?.[0]?.amount) { + amount = new BigNumber(sec.targets[0].amount); + receiver = sec.targets[0].owner; + } + if (!amount && sec.sources?.[0]?.amount) { + amount = new BigNumber(sec.sources[0].amount); } - // Fallback to sources - if (sec.sources?.length && sec.sources[0].amount && sec.sources[0].owner) { - return { - amount: new BigNumber(sec.sources[0].amount), - receiver: sec.sources[0].owner, - }; + if (!sender && sec.sources?.[0]?.owner) { + sender = sec.sources[0].owner; } + if (amount && (sender || receiver)) break; // we have what we need } - return undefined; + + return amount ? { amount, sender, receiver } : undefined; } export const TransactionCard = ({ tx }: Props): JSX.Element => { @@ -98,49 +101,140 @@ export const TransactionCard = ({ tx }: Props): JSX.Element => { toDisplayAmount(asset, txnInfo.amount) : undefined; const receiver = txnInfo?.receiver; + const sender = txnInfo?.sender; + const transactionFailed = transaction?.exitCode === "rejected"; + const api = useAtomValue(indexerApiAtom); + const [timestamp, setTimestamp] = useState(undefined); + + useEffect(() => { + const getBlockHeight = async (): Promise => { + // TODO: need to update the type on indexer + // @ts-expect-error need to update the type on indexer + if (transactionTopLevel?.blockHeight && api) { + try { + const timestamp = await fetchBlockByHeight( + api, + // @ts-expect-error need to update the type on indexer + transactionTopLevel.blockHeight + ); + console.log(timestamp, "timestamp"); + setTimestamp(timestamp); + } catch (error) { + console.error("Failed to fetch block height:", error); + } + } + }; + + getBlockHeight(); + // @ts-expect-error need to update the type on indexer + }, [api, transactionTopLevel?.blockHeight]); + + const renderKeplrIcon = (address: string): JSX.Element | null => { + if (isShieldedAddress(address)) return null; + if (isTransparentAddress(address)) return null; + return ; + }; return (
- - {isReceived && } - {!isReceived && } - - -
-

{titleFor(transaction?.kind, isReceived)}

-

- {" "} - to {receiver} -

+
+ + {isReceived && } + {!isReceived && } + + +
+

+ {titleFor(transaction?.kind, isReceived)}{" "} + {!transactionFailed && ( + + )} + {transactionFailed && ( + + )} +

+

+ {timestamp ? + new Date(timestamp * 1000) + .toLocaleString("en-US", { + day: "2-digit", + month: "2-digit", + year: "2-digit", + hour: "2-digit", + minute: "2-digit", + }) + .replace(",", "") + : "-"} +

+
+
+ +
+
+ +
+
- - {transaction?.exitCode === "applied" && ( - - )} - {transaction?.exitCode === "rejected" && ( - - )} - +
+

+ From +

+

+ {isShieldedAddress(sender ?? "") ? + + znam + + :
+ {renderKeplrIcon(sender ?? "")} + {shortenAddress(sender ?? "", 10, 10)} +
+ } +

+
+ +
+

+ To +

+

+ {isShieldedAddress(receiver ?? "") ? + + znam + + :
+ {renderKeplrIcon(receiver ?? "")} + {shortenAddress(receiver ?? "", 10, 10)} +
+ } +

+
); }; diff --git a/apps/namadillo/src/App/Transactions/TransactionDetails.tsx b/apps/namadillo/src/App/Transactions/TransactionDetails.tsx deleted file mode 100644 index 267fd2c884..0000000000 --- a/apps/namadillo/src/App/Transactions/TransactionDetails.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Panel } from "@namada/components"; -import { useSanitizedParams } from "@namada/hooks"; -import { InnerTransaction } from "@namada/indexer-client"; -import { TransactionReceipt } from "App/Common/TransactionReceipt"; -import { indexerApiAtom } from "atoms/api"; -import { useAtomValue } from "jotai"; -import { useEffect, useState } from "react"; -import { TransactionNotFoundPanel } from "./TransactionNotFoundPanel"; - -export const TransactionDetails = (): JSX.Element => { - const { hash } = useSanitizedParams(); - const api = useAtomValue(indexerApiAtom); - const [transaction, setTransaction] = useState(null); - const [loading, setLoading] = useState(true); - - useEffect(() => { - const fetchTransactionData = async (): Promise => { - try { - if (hash) { - const response = await api.apiV1ChainInnerTxIdGet(hash); - setTransaction(response.data); - } - } catch (error) { - console.error("Error fetching transaction:", error); - } finally { - setLoading(false); - } - }; - - fetchTransactionData(); - }, [hash, api]); - - if (loading) { - return ( - -

Transactions

-
Loading transaction details...
-
- ); - } - - if (!transaction) { - return ; - } - - return ( - -

Transactions

- -
- ); -}; diff --git a/apps/namadillo/src/App/Transactions/TransactionHistory.tsx b/apps/namadillo/src/App/Transactions/TransactionHistory.tsx index c46ac9260e..38c6f81109 100644 --- a/apps/namadillo/src/App/Transactions/TransactionHistory.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionHistory.tsx @@ -54,7 +54,7 @@ export const TransactionHistory = (): JSX.Element => { return historicalTransactions.slice(startIndex, endIndex); }, [historicalTransactions, currentPage]); - console.log(historicalTransactions); + console.log(historicalTransactions, "shitoryyy"); const renderRow = ( transaction: TransactionHistoryType, diff --git a/apps/namadillo/src/App/Transactions/TransactionNotFoundPanel.tsx b/apps/namadillo/src/App/Transactions/TransactionNotFoundPanel.tsx deleted file mode 100644 index a75119796d..0000000000 --- a/apps/namadillo/src/App/Transactions/TransactionNotFoundPanel.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Panel } from "@namada/components"; -import { shortenAddress } from "@namada/utils"; - -type TransactionNotFoundPanelProps = { - hash: string; -}; - -export const TransactionNotFoundPanel = ({ - hash, -}: TransactionNotFoundPanelProps): JSX.Element => { - return ( - -

Transactions

-

- The details of transaction{" "} - {shortenAddress(hash || "", 10, 6)}{" "} - couldn't be found on this device. -

-
- ); -}; diff --git a/apps/namadillo/src/atoms/balance/services.ts b/apps/namadillo/src/atoms/balance/services.ts index ade688c7f2..6f6c1e82ac 100644 --- a/apps/namadillo/src/atoms/balance/services.ts +++ b/apps/namadillo/src/atoms/balance/services.ts @@ -111,6 +111,14 @@ export const fetchBlockHeightByTimestamp = async ( return Number(response.data.height); }; +export const fetchBlockByHeight = async ( + api: DefaultApi, + height: number +): Promise => { + const response = await api.apiV1BlockHeightValueGet(height); + return Number(response.data.timestamp); +}; + export const fetchShieldedRewards = async ( viewingKey: DatedViewingKey, chainId: string, From dd3a9833b6504d9d4f06de0fe8698796e1d6d803 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 12 May 2025 12:48:33 +1200 Subject: [PATCH 10/17] fix: make it work as screen shrinks --- .../src/App/Transactions/TransactionCard.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/namadillo/src/App/Transactions/TransactionCard.tsx b/apps/namadillo/src/App/Transactions/TransactionCard.tsx index 70c098a6f1..7a2bfa5a49 100644 --- a/apps/namadillo/src/App/Transactions/TransactionCard.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionCard.tsx @@ -1,3 +1,4 @@ +import { CopyToClipboardControl } from "@namada/components"; import { TransactionHistory as TransactionHistoryType } from "@namada/indexer-client"; import { shortenAddress } from "@namada/utils"; import { TokenCurrency } from "App/Common/TokenCurrency"; @@ -139,7 +140,7 @@ export const TransactionCard = ({ tx }: Props): JSX.Element => {
{ > {titleFor(transaction?.kind, isReceived)}{" "} {!transactionFailed && ( - + )} {transactionFailed && ( - + )} +

{timestamp ? From 23c30682168ad85071a5fa6f31e9b2471076d24d Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 12 May 2025 13:22:42 +1200 Subject: [PATCH 11/17] fix: cleanup pending txns --- .../Transactions/PendingTransactionCard.tsx | 148 ++++++++++++------ .../src/App/Transactions/TransactionCard.tsx | 2 +- 2 files changed, 98 insertions(+), 52 deletions(-) diff --git a/apps/namadillo/src/App/Transactions/PendingTransactionCard.tsx b/apps/namadillo/src/App/Transactions/PendingTransactionCard.tsx index a8d88d8c64..2ca20b1322 100644 --- a/apps/namadillo/src/App/Transactions/PendingTransactionCard.tsx +++ b/apps/namadillo/src/App/Transactions/PendingTransactionCard.tsx @@ -1,17 +1,21 @@ +import { shortenAddress } from "@namada/utils"; import { TokenCurrency } from "App/Common/TokenCurrency"; -import { parseChainInfo } from "App/Transfer/common"; -import { chainRegistryAtom } from "atoms/integrations"; +import { AssetImage } from "App/Transfer/AssetImage"; +import { isShieldedAddress, isTransparentAddress } from "App/Transfer/common"; import clsx from "clsx"; -import { useAtomValue } from "jotai"; -import { GoIssueClosed, GoIssueTrackedBy, GoXCircle } from "react-icons/go"; -import { ImCheckmark } from "react-icons/im"; +import { FaLock } from "react-icons/fa"; +import { + IoArrowForward, + IoCheckmarkCircleOutline, + IoCloseCircleOutline, +} from "react-icons/io5"; import { twMerge } from "tailwind-merge"; import { ibcTransferStages, - IbcTransferTransactionData, namadaTransferStages, TransferTransactionData, } from "types"; +import keplrSvg from "../../integrations/assets/keplr.svg"; type TransactionCardProps = { transaction: TransferTransactionData; @@ -34,59 +38,101 @@ const getTitle = (transferTransaction: TransferTransactionData): string => { export const PendingTransactionCard = ({ transaction, }: TransactionCardProps): JSX.Element => { - const availableChains = useAtomValue(chainRegistryAtom); - const isIbc = Object.keys(ibcTransferStages).includes(transaction.type); - - const chainId = - isIbc ? - (transaction as IbcTransferTransactionData).destinationChainId - : transaction.chainId; - - const chainName = - chainId in availableChains ? - parseChainInfo(availableChains[chainId].chain)?.pretty_name - : chainId; - + const renderKeplrIcon = (address: string): JSX.Element | null => { + if (isShieldedAddress(address)) return null; + if (isTransparentAddress(address)) return null; + return ; + }; + const sender = transaction.sourceAddress; + const receiver = transaction.destinationAddress; return ( -
    -
  • -
    +
    + - + + +
    +

    - {transaction.status === "success" && } - {transaction.status === "pending" && } - {transaction.status === "error" && } - -
    -

    {getTitle(transaction)}

    -

    - {" "} - to {chainName} {transaction.destinationAddress} -

    -
    - - {transaction.status === "success" && } - -

    -
  • -
+ {getTitle(transaction)}{" "} + {transaction.status === "success" && ( + + )} + {transaction.status === "error" && ( + + )} +

+

Pending

+
+ + +
+
+ +
+ +
+
+

+ From +

+

+ {isShieldedAddress(sender ?? "") ? + + znam + + :
+ {renderKeplrIcon(sender ?? "")} + {shortenAddress(sender ?? "", 10, 10)} +
+ } +

+
+ +
+

+ To +

+

+ {isShieldedAddress(receiver ?? "") ? + + znam + + :
+ {renderKeplrIcon(receiver ?? "")} + {shortenAddress(receiver ?? "", 10, 10)} +
+ } +

+
+ ); }; diff --git a/apps/namadillo/src/App/Transactions/TransactionCard.tsx b/apps/namadillo/src/App/Transactions/TransactionCard.tsx index 7a2bfa5a49..d7debf0ec5 100644 --- a/apps/namadillo/src/App/Transactions/TransactionCard.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionCard.tsx @@ -201,7 +201,7 @@ export const TransactionCard = ({ tx }: Props): JSX.Element => { From e3324a9cd050a6d9db3ab88b78fa90612768f1b6 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 12 May 2025 13:26:23 +1200 Subject: [PATCH 12/17] fix: cleanup --- .../namadillo/src/atoms/transactions/atoms.ts | 28 ------------------- .../src/atoms/transactions/services.ts | 2 -- 2 files changed, 30 deletions(-) diff --git a/apps/namadillo/src/atoms/transactions/atoms.ts b/apps/namadillo/src/atoms/transactions/atoms.ts index 38d0fd30cf..97350a715a 100644 --- a/apps/namadillo/src/atoms/transactions/atoms.ts +++ b/apps/namadillo/src/atoms/transactions/atoms.ts @@ -41,34 +41,6 @@ export const fetchTransactionAtom = atom((get) => { return (hash: string) => fetchTransaction(api, hash); }); -// The original atom, kept for backward compatibility -export const chainTransactionHistoryAtom = atomWithQuery<{ - results: TransactionHistory[]; - pagination: Pagination; -}>((get) => { - const api = get(indexerApiAtom); - const accounts = get(allDefaultAccountsAtom); - const addresses = accounts.data?.map((acc) => acc.address); - return { - enabled: !!addresses, // Only run the query if we have addresses - queryKey: ["chain-transaction-history", addresses], - queryFn: async () => { - if (!addresses) { - return { - results: [], - pagination: { - totalPages: "0", - totalItems: "0", - currentPage: "0", - perPage: "0", - }, - }; - } - return fetchHistoricalTransactions(api, addresses); - }, - }; -}); - // New atom family for paginated transaction history export const chainTransactionHistoryFamily = atomFamily( (options?: { page?: number; perPage?: number; fetchAll?: boolean }) => diff --git a/apps/namadillo/src/atoms/transactions/services.ts b/apps/namadillo/src/atoms/transactions/services.ts index d8e276fcc4..bf05f82530 100644 --- a/apps/namadillo/src/atoms/transactions/services.ts +++ b/apps/namadillo/src/atoms/transactions/services.ts @@ -79,9 +79,7 @@ export const fetchHistoricalTransactions = async ( page?: number, perPage?: number ): Promise<{ results: TransactionHistory[]; pagination: Pagination }> => { - // indexer uses 1-based pagination const pageParam = page ? page : undefined; - const response = await api.apiV1ChainHistoryGet(addresses, { params: { page: pageParam, From fe0b49e5efc313dbd68b2f086b03e839e8826e67 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 12 May 2025 13:26:57 +1200 Subject: [PATCH 13/17] fix: cleanup pending txns --- apps/namadillo/src/hooks/useTransactionActions.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/apps/namadillo/src/hooks/useTransactionActions.ts b/apps/namadillo/src/hooks/useTransactionActions.ts index 8a43f10d2f..e75f2bafd3 100644 --- a/apps/namadillo/src/hooks/useTransactionActions.ts +++ b/apps/namadillo/src/hooks/useTransactionActions.ts @@ -8,7 +8,6 @@ import { Address, TransferTransactionData } from "types"; type UseTransactionActionsOutput = { transactions: TransferTransactionData[]; - findByHash: (hash: string) => TransferTransactionData | undefined; storeTransaction: (tx: TransferTransactionData) => void; changeTransaction: ( hash: string, @@ -56,13 +55,8 @@ export const useTransactionActions = (): UseTransactionActionsOutput => { }); }; - const findByHash = (hash: string): undefined | TransferTransactionData => { - return transactions.find((t) => t.hash === hash); - }; - return { transactions, - findByHash, storeTransaction, changeTransaction, }; From b022ed389b64f863449a3d1ae745dd620707b051 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 12 May 2025 13:27:36 +1200 Subject: [PATCH 14/17] fix: cleanup --- apps/namadillo/src/App/Transactions/TransactionHistory.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/apps/namadillo/src/App/Transactions/TransactionHistory.tsx b/apps/namadillo/src/App/Transactions/TransactionHistory.tsx index 38c6f81109..d44c6673a9 100644 --- a/apps/namadillo/src/App/Transactions/TransactionHistory.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionHistory.tsx @@ -26,9 +26,6 @@ export const transferKindOptions = [ "received", ]; -// NOTES: -// - WrapperID is the transaction hash -// - TxID is the inner transaction export const TransactionHistory = (): JSX.Element => { const [currentPage, setCurrentPage] = useState(0); const pending = useAtomValue(pendingTransactionsHistoryAtom); @@ -54,8 +51,6 @@ export const TransactionHistory = (): JSX.Element => { return historicalTransactions.slice(startIndex, endIndex); }, [historicalTransactions, currentPage]); - console.log(historicalTransactions, "shitoryyy"); - const renderRow = ( transaction: TransactionHistoryType, index: number From 5620173e649420bf219720f746561e7d6c49ad6b Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 12 May 2025 13:28:55 +1200 Subject: [PATCH 15/17] fix: cleanup[ --- apps/namadillo/src/App/Transactions/TransactionCard.tsx | 9 ++++----- apps/namadillo/src/atoms/balance/services.ts | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/namadillo/src/App/Transactions/TransactionCard.tsx b/apps/namadillo/src/App/Transactions/TransactionCard.tsx index d7debf0ec5..96eef16f93 100644 --- a/apps/namadillo/src/App/Transactions/TransactionCard.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionCard.tsx @@ -5,7 +5,7 @@ import { TokenCurrency } from "App/Common/TokenCurrency"; import { AssetImage } from "App/Transfer/AssetImage"; import { isShieldedAddress, isTransparentAddress } from "App/Transfer/common"; import { indexerApiAtom } from "atoms/api"; -import { fetchBlockByHeight } from "atoms/balance/services"; +import { fetchBlockTimestampByHeight } from "atoms/balance/services"; import { chainAssetsMapAtom } from "atoms/chain"; import BigNumber from "bignumber.js"; import clsx from "clsx"; @@ -108,17 +108,16 @@ export const TransactionCard = ({ tx }: Props): JSX.Element => { const [timestamp, setTimestamp] = useState(undefined); useEffect(() => { - const getBlockHeight = async (): Promise => { + const getBlockTimestamp = async (): Promise => { // TODO: need to update the type on indexer // @ts-expect-error need to update the type on indexer if (transactionTopLevel?.blockHeight && api) { try { - const timestamp = await fetchBlockByHeight( + const timestamp = await fetchBlockTimestampByHeight( api, // @ts-expect-error need to update the type on indexer transactionTopLevel.blockHeight ); - console.log(timestamp, "timestamp"); setTimestamp(timestamp); } catch (error) { console.error("Failed to fetch block height:", error); @@ -126,7 +125,7 @@ export const TransactionCard = ({ tx }: Props): JSX.Element => { } }; - getBlockHeight(); + getBlockTimestamp(); // @ts-expect-error need to update the type on indexer }, [api, transactionTopLevel?.blockHeight]); diff --git a/apps/namadillo/src/atoms/balance/services.ts b/apps/namadillo/src/atoms/balance/services.ts index 6f6c1e82ac..fec3d06b63 100644 --- a/apps/namadillo/src/atoms/balance/services.ts +++ b/apps/namadillo/src/atoms/balance/services.ts @@ -111,7 +111,7 @@ export const fetchBlockHeightByTimestamp = async ( return Number(response.data.height); }; -export const fetchBlockByHeight = async ( +export const fetchBlockTimestampByHeight = async ( api: DefaultApi, height: number ): Promise => { From a73d39d1595ae82bc3324f01dcceff539af1583f Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 12 May 2025 13:36:14 +1200 Subject: [PATCH 16/17] fix: update boldness of font --- apps/namadillo/src/App/Transactions/TransactionCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/namadillo/src/App/Transactions/TransactionCard.tsx b/apps/namadillo/src/App/Transactions/TransactionCard.tsx index 96eef16f93..b71fc4f22d 100644 --- a/apps/namadillo/src/App/Transactions/TransactionCard.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionCard.tsx @@ -139,7 +139,7 @@ export const TransactionCard = ({ tx }: Props): JSX.Element => {
Date: Tue, 13 May 2025 13:03:39 +1200 Subject: [PATCH 17/17] fix: title --- apps/namadillo/src/App/Transactions/TransactionCard.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/namadillo/src/App/Transactions/TransactionCard.tsx b/apps/namadillo/src/App/Transactions/TransactionCard.tsx index b71fc4f22d..ef4d8d38b0 100644 --- a/apps/namadillo/src/App/Transactions/TransactionCard.tsx +++ b/apps/namadillo/src/App/Transactions/TransactionCard.tsx @@ -54,8 +54,8 @@ const titleFor = (kind: string | undefined, isReceived: boolean): string => { if (isReceived) return "Receive"; if (kind.startsWith(IBC_PREFIX)) return "IBC Transfer"; if (kind === "transparentTransfer") return "Transparent Transfer"; - if (kind === "shieldingTransfer" || kind === "unshieldingTransfer") - return "Shielding Transfer"; + if (kind === "shieldingTransfer") return "Shielding Transfer"; + if (kind === "unshieldingTransfer") return "Unshielding Transfer"; if (kind === "shieldedTransfer") return "Shielded Transfer"; return "Transfer"; };