Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions src/components/AccountTransactions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import { Hash } from "./Hash";
import { PageLink } from "./PageLink";

interface Tx {
txId: string;
txId?: string; // Legacy field
id?: string; // New field
wrapperId: string;
kind: string;
data: string;
Expand Down Expand Up @@ -156,7 +157,7 @@ export const AccountTransactions = ({ address }: AccountTransactionsProps) => {

if (error) {
return (
<Box bg="red.100" color="red.800" p={4} rounded="md">
<Box bg="red.700" color="white" p={4} rounded="md">
<Text fontWeight="semibold">Error</Text>
<Text>Failed to load transactions. Please try again.</Text>
</Box>
Expand All @@ -179,6 +180,7 @@ export const AccountTransactions = ({ address }: AccountTransactionsProps) => {
<Table.Row>
<Table.ColumnHeader color="gray.300">Hash</Table.ColumnHeader>
<Table.ColumnHeader color="gray.300">Type</Table.ColumnHeader>
<Table.ColumnHeader color="gray.300">Direction</Table.ColumnHeader>
<Table.ColumnHeader color="gray.300">Status</Table.ColumnHeader>
<Table.ColumnHeader color="gray.300">Block</Table.ColumnHeader>
<Table.ColumnHeader color="gray.300">Amount</Table.ColumnHeader>
Expand All @@ -193,7 +195,7 @@ export const AccountTransactions = ({ address }: AccountTransactionsProps) => {
const tokenSymbol = token ? chainAssetsMap[token]?.symbol : "";
return (
<Table.Row
key={tx.tx.txId}
key={tx.tx.txId || tx.tx.id}
_hover={{
bg: "gray.800",
}}
Expand All @@ -206,8 +208,8 @@ export const AccountTransactions = ({ address }: AccountTransactionsProps) => {
</Table.Cell>
<Table.Cell>
<Badge
variant="subtle"
colorScheme="gray"
variant="outline"
colorPalette="gray"
fontSize="xs"
textTransform="capitalize"
fontWeight="medium"
Expand All @@ -217,9 +219,21 @@ export const AccountTransactions = ({ address }: AccountTransactionsProps) => {
</Table.Cell>
<Table.Cell>
<Badge
variant="subtle"
backgroundColor={
tx.tx.exitCode === "applied" ? "green.500" : "red.500"
variant={
tx.kind === "received" ? "solid" : "outline"
}
colorPalette="blue"
fontSize="xs"
textTransform="capitalize"
>
{tx.kind || "-"}
</Badge>
</Table.Cell>
<Table.Cell>
<Badge
variant="surface"
colorPalette={
tx.tx.exitCode === "applied" ? "green" : "red"
}
fontSize="xs"
textTransform="capitalize"
Expand Down
2 changes: 1 addition & 1 deletion src/components/InnerTransactionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export const InnerTransactionCard = ({
>
<Data
title="Inner TX Hash"
content={<Hash hash={innerTransaction.txId} enableCopy={true} />}
content={<Hash hash={innerTransaction.txId || innerTransaction.id || ""} enableCopy={true} />}
/>
<Data
title="Exit Code"
Expand Down
197 changes: 197 additions & 0 deletions src/components/InnerTransactionListCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import { Grid, VStack, Text, Tooltip } from "@chakra-ui/react";
import { useNavigate } from "react-router";
import { Data } from "./Data";
import { Hash } from "./Hash";
import { TransactionStatusBadge } from "./TransactionStatusBadge";
import { transactionUrl } from "../routes";
import { useChainAssetsMap } from "../queries/useChainAssetsMap";
import { toDisplayAmount, formatNumberWithCommasAndDecimals, getAgeFromTimestamp, formatTimestamp } from "../utils";
import type { Asset } from "@chain-registry/types";
import BigNumber from "bignumber.js";
import { useBlockInfo } from "../queries/useBlockInfo";

type InnerTx = {
id: string;
kind?: string;
exitCode?: string;
data?: string;
};

type InnerTransactionListCardProps = {
inner: InnerTx;
};

export const InnerTransactionListCard = ({ inner }: InnerTransactionListCardProps) => {
const navigate = useNavigate();
const { data: chainAssetsMap } = useChainAssetsMap();
const blockInfo = useBlockInfo((inner as any).blockHeight);

// Parse token and amount for transfer types
const getTransferInfo = () => {
if (!inner.data || !inner.kind) return { token: null, amount: null };

const transferKinds = [
"transparentTransfer",
"shieldingTransfer",
"unshieldingTransfer",
"ibcShieldingTransfer",
"ibcUnshieldingTransfer",
"ibcTransparentTransfer",
];

if (!transferKinds.includes(inner.kind)) return { token: null, amount: null };

try {
const parsedData = typeof inner.data === "string" ? JSON.parse(inner.data) : inner.data;
let token: string | null = null;
let amount: string | null = null;

if (Array.isArray(parsedData)) {
const sourceSection = parsedData.find((section: any) => section.sources);
const targetSection = parsedData.find((section: any) => section.targets);
if (sourceSection?.sources?.[0]) {
token = sourceSection.sources[0].token;
amount = sourceSection.sources[0].amount;
}
if (targetSection?.targets?.[0] && !amount) {
token = targetSection.targets[0].token;
amount = targetSection.targets[0].amount;
}
} else if (parsedData?.sources?.[0] || parsedData?.targets?.[0]) {
if (parsedData.sources?.[0]) {
token = parsedData.sources[0].token;
amount = parsedData.sources[0].amount;
}
if (parsedData.targets?.[0] && !amount) {
token = parsedData.targets[0].token;
amount = parsedData.targets[0].amount;
}
}

return { token, amount };
} catch {
return { token: null, amount: null };
}
};

const { token, amount } = getTransferInfo();
const tokenAsset = token ? chainAssetsMap?.[token] : null;

const transferKinds = [
"transparentTransfer",
"shieldingTransfer",
"unshieldingTransfer",
"ibcShieldingTransfer",
"ibcUnshieldingTransfer",
"ibcTransparentTransfer",
];
const isTransferType = inner.kind && transferKinds.includes(inner.kind);

return (
<Grid
gap={2}
w="100%"
bg="gray.800"
py={4}
px={6}
rounded="sm"
templateColumns="1fr 1fr 3fr 1fr 0.75fr 1.25fr 1fr"
cursor="pointer"
_hover={{ bg: "gray.700" }}
onClick={() => navigate(transactionUrl(inner.id))}
>
<Data
title="Age"
content={
blockInfo.data?.timestamp ? (
<Tooltip.Root openDelay={0} closeDelay={0}>
<Tooltip.Trigger asChild>
<Text fontSize="sm" color="gray.400">
{getAgeFromTimestamp(blockInfo.data.timestamp)}
</Text>
</Tooltip.Trigger>
<Tooltip.Positioner>
<Tooltip.Content bg="gray.700" color="white" px={2} py={1} rounded="md" fontSize="sm">
{formatTimestamp(parseInt(blockInfo.data.timestamp, 10))}
</Tooltip.Content>
</Tooltip.Positioner>
</Tooltip.Root>
) : (
<Text fontSize="sm" color="gray.500">-</Text>
)
}
/>
<Data
title="Block #"
content={
<Text fontSize="sm" color="gray.400">
{(inner as any).blockHeight ? (inner as any).blockHeight.toLocaleString() : "-"}
</Text>
}
/>
<Data
title="Hash"
content={
<VStack align="start" gap={1}>
<Text fontSize="sm" color="gray.400">
{inner.id ? `${inner.id.slice(0, 6)}...${inner.id.slice(-6)}` : "-"}
</Text>
</VStack>
}
/>
<Data
title={isTransferType ? "Amount" : ""}
content={
isTransferType ? (
amount && tokenAsset ? (
<Text fontSize="sm" color="gray.400">
{formatNumberWithCommasAndDecimals(toDisplayAmount(
tokenAsset as Asset,
new BigNumber(amount),
))}
</Text>
) : amount ? (
<Text fontSize="sm" color="gray.400">{amount}</Text>
) : (
<Text color="gray.500">-</Text>
)
) : (
<Text color="transparent">-</Text>
)
}
/>
<Data
title={isTransferType ? "Token" : ""}
content={
isTransferType ? (
tokenAsset ? (
<Text fontSize="sm" fontWeight="medium" color="gray.400">
{tokenAsset.symbol}
</Text>
) : token ? (
<Hash hash={token} />
) : (
<Text color="gray.500">-</Text>
)
) : (
<Text color="transparent">-</Text>
)
}
/>
<Data
title="Kind"
content={
<Text fontSize="sm" color="gray.400">
{inner.kind || "-"}
</Text>
}
/>
<Data
title="Status"
content={<TransactionStatusBadge exitCode={inner.exitCode || "unknown"} />}
/>
</Grid>
);
};


4 changes: 1 addition & 3 deletions src/components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@ export const Search = () => {
const [isFocused, setIsFocused] = useState(false);
const [debouncedSearch] = useDebounce(searchValue, 500);
const search = useSearchValue(debouncedSearch.toLowerCase());
const { matches: matchedValidators } = useValidatorNameMatchesFuzzy(
debouncedSearch,
);
const { matches: matchedValidators } = useValidatorNameMatchesFuzzy(debouncedSearch);
const emptyResults =
search.isSuccess &&
search.data.transactions.length +
Expand Down
43 changes: 39 additions & 4 deletions src/components/TransactionCard.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { useTransaction } from "../queries/useTransaction";
import { Box, Grid, Skeleton, VStack } from "@chakra-ui/react";
import { Box, Grid, Skeleton, VStack, Text, Tooltip } from "@chakra-ui/react";
import { Data } from "./Data";
import { Hash } from "./Hash";
import { AccountLink } from "./AccountLink";
import { useNavigate } from "react-router";
import { TransactionStatusBadge } from "./TransactionStatusBadge";
import { transactionUrl } from "../routes";
import { getAgeFromTimestamp, formatTimestamp } from "../utils";
import { useBlockInfo } from "../queries/useBlockInfo";
import type { InnerTransaction } from "../types";

type TransactionListProps = {
Expand All @@ -29,6 +30,7 @@ const allInnerTxApplied = (innerTransactions: InnerTransaction[]): string => {
export const TransactionCard = ({ hash }: TransactionListProps) => {
const navigate = useNavigate();
const transaction = useTransaction(hash);
const blockInfo = useBlockInfo(transaction.data?.blockHeight);

if (transaction.isLoading) {
return <Skeleton h="60px" w="100%" />;
Expand All @@ -46,16 +48,49 @@ export const TransactionCard = ({ hash }: TransactionListProps) => {
py={4}
px={6}
rounded="sm"
templateColumns="3fr 1fr 1fr 1fr 1fr"
templateColumns="1fr 1fr 2fr 1fr 1fr 1fr 2fr"
cursor="pointer"
_hover={{ bg: "gray.700" }}
onClick={() => navigate(transactionUrl(hash))}
>
<Data
title="Age"
content={
blockInfo.data?.timestamp ? (
<Tooltip.Root openDelay={0} closeDelay={0}>
<Tooltip.Trigger asChild>
<Text fontSize="sm" color="gray.400">
{getAgeFromTimestamp(blockInfo.data.timestamp)}
</Text>
</Tooltip.Trigger>
<Tooltip.Positioner>
<Tooltip.Content bg="gray.700" color="white" px={2} py={1} rounded="md" fontSize="sm">
{formatTimestamp(parseInt(blockInfo.data.timestamp, 10))}
</Tooltip.Content>
</Tooltip.Positioner>
</Tooltip.Root>
) : (
<Text fontSize="sm" color="gray.500">-</Text>
)
}
/>
<Data
title="Block #"
content={
<Text fontSize="sm" color="gray.400">
{transaction.data?.blockHeight ? transaction.data.blockHeight.toLocaleString() : "-"}
</Text>
}
/>
<Data
title="Hash"
content={
<VStack align="start" gap={1}>
<Hash hash={transaction.data?.txId} />
<Text fontSize="sm" color="gray.400">
{(transaction.data?.txId || transaction.data?.id) ?
`${(transaction.data?.txId || transaction.data?.id)!.slice(0, 6)}...${(transaction.data?.txId || transaction.data?.id)!.slice(-6)}` :
"-"}
</Text>
</VStack>
}
/>
Expand Down
2 changes: 1 addition & 1 deletion src/components/TransactionDetailsData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type WrapperTxContext = {
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
const createValueMap = (wrapperContext?: WrapperTxContext): Record<string, Function | undefined> => {
const { data: chainAssetsMap } = useChainAssetsMap();
const { data: validators } = useAllValidators();
const { data: validators } = useAllValidators({ refetchInterval: undefined });

return {
validator: (address: string) => {
Expand Down
4 changes: 2 additions & 2 deletions src/pages/Account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const Account = () => {
useDelegations(address!);

const { data: transactionsData } = useAccountTransactions(address ?? "");
const allValidators = useAllValidators();
const allValidators = useAllValidators({ refetchInterval: undefined });
const matchingValidator: Validator | undefined = useMemo(() => {
const list = (allValidators.data as Validator[]) || [];
return list.find((v: Validator) => v.address === address);
Expand Down Expand Up @@ -123,7 +123,7 @@ export const Account = () => {

if (accountError) {
return (
<Box bg="red.100" color="red.800" p={4} rounded="md">
<Box bg="red.700" color="white" p={4} rounded="md">
<Text fontWeight="semibold">Error</Text>
<Text>
Failed to load account details. Please check the address and try
Expand Down
Loading