Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CFE-612]: Feat - contract details EVM txs #1127

Merged
merged 14 commits into from
Sep 5, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Features

- [#1127](https://github.com/alleslabs/celatone-frontend/pull/1127) Add EVM contract details EVM transactions
- [#1120](https://github.com/alleslabs/celatone-frontend/pull/1120) Add EVM contract details Cosmos transactions
- [#1130](https://github.com/alleslabs/celatone-frontend/pull/1130) Support EVM contract and transaction hash in the main search
- [#1128](https://github.com/alleslabs/celatone-frontend/pull/1128) Implement EVM tx details
Expand Down
2 changes: 2 additions & 0 deletions src/lib/app-provider/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export enum CELATONE_QUERY_KEYS {
// TX
TX_DATA = "CELATONE_QUERY_TX_DATA",
TX_DATA_JSON_RPC = "CELATONE_QUERY_TX_DATA_JSON_RPC",
TXS_DATA_JSON_RPC = "CELATONE_QUERY_TXS_DATA_JSON_RPC",
TXS_BY_ADDRESS = "CELATONE_QUERY_TXS_BY_ADDRESS",
TXS_COUNT_SEQUENCER = "CELATONE_QUERY_TXS_COUNT_SEQUENCER",
TXS_COUNT_BY_ADDRESS = "CELATONE_QUERY_TXS_COUNT_BY_ADDRESS",
Expand Down Expand Up @@ -144,6 +145,7 @@ export enum CELATONE_QUERY_KEYS {
EVM_CODES_BY_ADDRESS_LCD = "CELATONE_QUERY_EVM_CODES_BY_ADDRESS_LCD",
EVM_CONTRACT_INFO_SEQUENCER = "CELATONE_QUERY_EVM_CONTRACT_INFO_SEQUENCER",
EVM_TX_HASH_BY_COSMOS_TX_HASH = "CELATONE_QUERY_EVM_TX_HASH_BY_COSMOS_TX_HASH",
EVM_TX_HASHES_BY_COSMOS_TX_HASHES = "CELATONE_QUERY_EVM_TX_HASHES_BY_COSMOS_TX_HASHES",
EVM_DENOM_BY_ADDRESS_LCD = "CELATONE_QUERY_EVM_DENOM_BY_ADDRESS_LCD",
COSMOS_TX_HASH_BY_EVM_TX_HASH = "CELATONE_QUERY_COSMOS_TX_HASH_BY_EVM_TX_HASH",
// RESOURCE
Expand Down
12 changes: 10 additions & 2 deletions src/lib/components/table/evm-transactions/EvmTransactionsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ interface EvmTransactionsTableProps {
evmTransactions: Option<TxDataWithTimeStampJsonRpc[]>;
isLoading: boolean;
emptyState: JSX.Element;
showTimestamp?: boolean;
}

export const EvmTransactionsTable = ({
evmTransactions,
isLoading,
emptyState,
showTimestamp = false,
}: EvmTransactionsTableProps) => {
const isMobile = useMobile();
const { data: evmParams, isLoading: isEvmParamsLoading } = useEvmParams();
Expand All @@ -39,8 +41,9 @@ export const EvmTransactionsTable = ({
"48px",
"minmax(180px, 2fr)",
"minmax(250px, 1fr)",
showTimestamp ? "minmax(247px, 1fr)" : "",
];
const templateColumns: string = columns.join(" ");
const templateColumns: string = columns.join(" ").trim();

return isMobile ? (
<MobileTableContainer>
Expand All @@ -50,19 +53,24 @@ export const EvmTransactionsTable = ({
evmTransaction={evmTransaction}
evmDenom={evmParams?.params.fee_denom}
assetInfos={assetInfos}
showTimestamp={showTimestamp}
/>
))}
</MobileTableContainer>
) : (
<TableContainer>
<EvmTransactionsTableHeader templateColumns={templateColumns} />
<EvmTransactionsTableHeader
templateColumns={templateColumns}
showTimestamp={showTimestamp}
/>
{evmTransactions.map((evmTransaction) => (
<EvmTransactionsTableRow
key={evmTransaction.tx.hash}
templateColumns={templateColumns}
evmTransaction={evmTransaction}
evmDenom={evmParams?.params.fee_denom}
assetInfos={assetInfos}
showTimestamp={showTimestamp}
/>
))}
</TableContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import { Grid } from "@chakra-ui/react";

import { TableHeader } from "../tableComponents";

interface EvmTransactionsTableHeaderProps {
templateColumns: GridProps["templateColumns"];
showTimestamp: boolean;
}

export const EvmTransactionsTableHeader = ({
templateColumns,
}: {
templateColumns: GridProps["templateColumns"];
}) => (
showTimestamp,
}: EvmTransactionsTableHeaderProps) => (
<Grid templateColumns={templateColumns} minW="min-content">
<TableHeader />
<TableHeader>Transaction Hash</TableHeader>
Expand All @@ -17,5 +21,6 @@ export const EvmTransactionsTableHeader = ({
<TableHeader />
<TableHeader>To</TableHeader>
<TableHeader>Amount</TableHeader>
{showTimestamp && <TableHeader>Timestamp</TableHeader>}
</Grid>
);
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import type { TxDataWithTimeStampJsonRpc } from "lib/services/types";
import type { AssetInfos, Option } from "lib/types";
import {
coinToTokenWithValue,
dateFromNow,
formatEvmTxHash,
formatPrice,
formatUTC,
formatUTokenWithPrecision,
getTokenLabel,
} from "lib/utils";
Expand All @@ -22,11 +24,13 @@ interface EvmTransactionsTableMobileCardProps {
evmTransaction: TxDataWithTimeStampJsonRpc;
evmDenom: Option<string>;
assetInfos: Option<AssetInfos>;
showTimestamp: boolean;
}
export const EvmTransactionsTableMobileCard = ({
evmTransaction,
evmDenom,
assetInfos,
showTimestamp,
}: EvmTransactionsTableMobileCardProps) => {
const navigate = useInternalNavigate();

Expand All @@ -45,20 +49,22 @@ export const EvmTransactionsTableMobileCard = ({
}
topContent={
<Flex align="center" gap={2} w="full">
{evmTransaction.txReceipt.status ? (
<CustomIcon name="check" color="success.main" />
) : (
<CustomIcon name="close" color="error.main" />
)}
<Flex direction="column" flex={3}>
<Flex direction="column" flex={3} gap={1}>
<MobileLabel label="Transaction Hash" />
<ExplorerLink
value={formatEvmTxHash(evmTransaction.tx.hash)}
type="tx_hash"
showCopyOnHover
/>
<Flex gap={1} alignItems="center">
{evmTransaction.txReceipt.status ? (
<CustomIcon name="check" color="success.main" />
) : (
<CustomIcon name="close" color="error.main" />
)}
<ExplorerLink
value={formatEvmTxHash(evmTransaction.tx.hash)}
type="tx_hash"
showCopyOnHover
/>
</Flex>
</Flex>
<Flex direction="column" flex={2}>
<Flex direction="column" flex={2} gap={1}>
<MobileLabel label="Method" />
<EvmMethodChip txInput={evmTransaction.tx.input} width="120px" />
</Flex>
Expand All @@ -67,15 +73,15 @@ export const EvmTransactionsTableMobileCard = ({
middleContent={
<>
<Flex>
<Flex direction="column" flex={1}>
<Flex direction="column" flex={1} gap={1}>
<MobileLabel label="sender" />
<ExplorerLink
value={evmTransaction.tx.from}
type="user_address"
showCopyOnHover
/>
</Flex>
<Flex direction="column" flex={1}>
<Flex direction="column" flex={1} gap={1}>
<MobileLabel label="To" />
<EvmToCell
to={evmTransaction.tx.to}
Expand Down Expand Up @@ -105,6 +111,19 @@ export const EvmTransactionsTableMobileCard = ({
</Flex>
</>
}
bottomContent={
showTimestamp &&
evmTransaction.timestamp && (
<Flex direction="column">
<Text variant="body2" color="text.dark">
{formatUTC(evmTransaction.timestamp)}
</Text>
<Text variant="body3" color="text.disabled">
({dateFromNow(evmTransaction.timestamp)})
</Text>
</Flex>
)
}
/>
);
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Grid, Text } from "@chakra-ui/react";
import { Flex, Grid, Text } from "@chakra-ui/react";
import { isUndefined } from "lodash";

import { TableRow } from "../tableComponents";
Expand All @@ -11,8 +11,10 @@ import type { TxDataWithTimeStampJsonRpc } from "lib/services/types";
import type { AssetInfos, Option } from "lib/types";
import {
coinToTokenWithValue,
dateFromNow,
formatEvmTxHash,
formatPrice,
formatUTC,
formatUTokenWithPrecision,
getTokenLabel,
} from "lib/utils";
Expand All @@ -22,13 +24,15 @@ interface EvmTransactionsTableRowProps {
evmTransaction: TxDataWithTimeStampJsonRpc;
evmDenom: Option<string>;
assetInfos: Option<AssetInfos>;
showTimestamp: boolean;
}

export const EvmTransactionsTableRow = ({
templateColumns,
evmTransaction,
evmDenom,
assetInfos,
showTimestamp,
}: EvmTransactionsTableRowProps) => {
const navigate = useInternalNavigate();

Expand Down Expand Up @@ -101,6 +105,24 @@ export const EvmTransactionsTableRow = ({
{!isUndefined(token.value) ? `(${formatPrice(token.value)})` : "N/A"}
</Text>
</TableRow>
{showTimestamp && (
<TableRow>
{evmTransaction.timestamp ? (
<Flex direction="column">
<Text variant="body2" color="text.dark">
{formatUTC(evmTransaction.timestamp)}
</Text>
<Text variant="body3" color="text.disabled">
({dateFromNow(evmTransaction.timestamp)})
</Text>
</Flex>
) : (
<Text variant="body2" color="text.disabled">
-
</Text>
)}
</TableRow>
)}
</Grid>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Text } from "@chakra-ui/react";

import { LoadNext } from "lib/components/LoadNext";
import { EmptyState } from "lib/components/state";
import { TransactionsTable, ViewMore } from "lib/components/table";
import type { Option, Transaction } from "lib/types";

interface EvmContractDetailsCosmosTxsProps {
onViewMore?: () => void;
txsData: Option<Transaction[]>;
isTxsLoading: boolean;
hasNextPage: Option<boolean>;
fetchNextPage: () => void;
isFetchingNextPage: boolean;
}

export const EvmContractDetailsCosmosTxs = ({
onViewMore,
txsData,
isTxsLoading,
hasNextPage,
fetchNextPage,
isFetchingNextPage,
}: EvmContractDetailsCosmosTxsProps) => (
<>
<TransactionsTable
transactions={!onViewMore ? txsData : txsData?.slice(0, 5)}
isLoading={isTxsLoading}
emptyState={
<EmptyState
imageVariant="empty"
message="There are no transactions on this contract."
/>
}
showRelations={false}
/>
{txsData && (
<>
{!onViewMore && (
<Text variant="body2" color="text.dark" mt={2}>
{txsData?.length ?? 0} Cosmos transactions found
</Text>
)}
{hasNextPage && (
<>
{onViewMore ? (
<ViewMore onClick={onViewMore} />
) : (
<LoadNext
text="Load more 10 transactions"
fetchNextPage={fetchNextPage}
isFetchingNextPage={isFetchingNextPage}
/>
)}
</>
)}
</>
)}
</>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Spinner, Text } from "@chakra-ui/react";

import { LoadNext } from "lib/components/LoadNext";
import { EmptyState } from "lib/components/state";
import { EvmTransactionsTable, ViewMore } from "lib/components/table";
import type { TxDataWithTimeStampJsonRpc } from "lib/services/types";
import type { Option, Transaction } from "lib/types";

interface EvmContractDetailsEvmTxsProps {
onViewMore?: () => void;
evmTxsData: Option<TxDataWithTimeStampJsonRpc[]>;
isEvmTxsDataLoading: boolean;
txsData: Option<Transaction[]>;
hasNextPage: Option<boolean>;
fetchNextPage: () => void;
isFetchingNextPage: boolean;
isTxsFetching: boolean;
isEvmTxsDataFetching: boolean;
}

export const EvmContractDetailsEvmTxs = ({
onViewMore,
evmTxsData,
isEvmTxsDataLoading,
txsData,
hasNextPage,
fetchNextPage,
isFetchingNextPage,
isTxsFetching,
isEvmTxsDataFetching,
}: EvmContractDetailsEvmTxsProps) => (
<>
<EvmTransactionsTable
evmTransactions={!onViewMore ? evmTxsData : evmTxsData?.slice(0, 5)}
isLoading={isEvmTxsDataLoading}
emptyState={
<EmptyState
imageVariant="empty"
message="There are no EVM transactions on this contract."
/>
}
showTimestamp
/>
{evmTxsData && (
<>
{!onViewMore && (
<Text variant="body2" color="text.dark" mt={2}>
{isTxsFetching || isEvmTxsDataFetching ? (
<Spinner as="span" size="xs" mr={1} />
) : (
evmTxsData.length
)}{" "}
EVM Txs found from {txsData?.length ?? 0} Cosmos Txs
</Text>
)}
{hasNextPage && (
<>
{onViewMore ? (
<ViewMore onClick={onViewMore} />
) : (
<LoadNext
text="Load more transactions"
fetchNextPage={fetchNextPage}
isFetchingNextPage={isFetchingNextPage}
/>
)}
</>
)}
</>
)}
</>
);
Loading