Merge pull request #198 from ArtemKolodko/refactor_txs_columns

Refactor transaction columns
pull/199/head
Artem 2 years ago committed by GitHub
commit 83d83890bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .gitignore
  2. 7
      src/components/block/BlockDetails.tsx
  3. 8
      src/components/block/BlockList.tsx
  4. 1
      src/components/block/helpers.tsx
  5. 1
      src/components/metrics/index.tsx
  6. 28
      src/components/tables/TransactionsTable.tsx
  7. 13
      src/components/transaction/TransactionDetails.tsx
  8. 1
      src/components/transaction/helpers.tsx
  9. 71
      src/components/ui/Address.tsx
  10. 10
      src/components/ui/ONEValue.tsx
  11. 4
      src/components/ui/Pagination/index.tsx
  12. 39
      src/components/ui/Tooltip.tsx
  13. 23
      src/index.css
  14. 18
      src/pages/AddressPage/AddressDetails.tsx
  15. 1
      src/pages/AddressPage/TokenInfo.tsx
  16. 2
      src/pages/AddressPage/index.tsx
  17. 15
      src/pages/AddressPage/tabs/events/Events.tsx
  18. 10
      src/pages/AddressPage/tabs/transactions/Transactions.tsx
  19. 164
      src/pages/AddressPage/tabs/transactions/columns/common.tsx
  20. 141
      src/pages/AddressPage/tabs/transactions/columns/erc20.tsx
  21. 11
      src/pages/AddressPage/tabs/transactions/columns/index.tsx
  22. 160
      src/pages/AddressPage/tabs/transactions/columns/nft.tsx
  23. 122
      src/pages/AddressPage/tabs/transactions/columns/staking.tsx
  24. 177
      src/pages/AddressPage/tabs/transactions/columns/transactions.tsx
  25. 778
      src/pages/AddressPage/tabs/txsColumns.tsx
  26. 11
      src/pages/AllBlocksPage/AllBlocksTable.tsx
  27. 12
      src/pages/ERC1155List/ERC1155Table.tsx
  28. 18
      src/pages/ERC20List/ERC20Table.tsx
  29. 12
      src/pages/ERC721List/ERC721Table.tsx
  30. 6
      src/pages/ExportData/index.tsx
  31. 7
      src/pages/MainPage/LatestBlocksTable.tsx
  32. 9
      src/pages/MainPage/LatestTransactionsTable.tsx
  33. 16
      src/pages/tools/CheckHRC/index.tsx
  34. 45
      src/theme.ts

1
.gitignore vendored

@ -6,6 +6,7 @@
.pnp.js .pnp.js
.firebase/* .firebase/*
/.firebase /.firebase
/firebase

@ -20,7 +20,6 @@ const columns = [
<Tip <Tip
dropProps={{ align: { left: "right" } }} dropProps={{ align: { left: "right" } }}
content={<TipContent message={blockPropertyDescriptions[e.key]} />} content={<TipContent message={blockPropertyDescriptions[e.key]} />}
plain
> >
<span> <span>
<CircleQuestion size="small" /> <CircleQuestion size="small" />
@ -101,12 +100,10 @@ export const BlockDetails: FunctionComponent<BlockDetailsProps> = ({
data={blockData} data={blockData}
step={10} step={10}
border={{ border={{
header: { header: false,
color: "none",
},
body: { body: {
color: "border", color: "border",
side: "top", side: "bottom",
size: "1px", size: "1px",
}, },
}} }}

@ -71,12 +71,10 @@ export const BlockList: FunctionComponent<BlockDetailsProps> = ({ block }) => {
data={blockData} data={blockData}
step={10} step={10}
border={{ border={{
header: { header: false,
color: 'none'
},
body: { body: {
color: 'border', color: 'border',
side: 'top', side: 'bottom',
size: '1px' size: '1px'
} }
}} }}
@ -91,4 +89,4 @@ export const BlockList: FunctionComponent<BlockDetailsProps> = ({ block }) => {
</Anchor> </Anchor>
</Box> </Box>
</> </>
} }

@ -58,6 +58,7 @@ export const blockPropertyDisplayNames: Record<string, string> = {
}; };
export const blockPropertyDescriptions: Record<string, string> = { export const blockPropertyDescriptions: Record<string, string> = {
shard: 'Shard Number',
number: number:
"Also known as Block Number. The block height, which indicates the length of the blockchain, increases after the addition of the new block.", "Also known as Block Number. The block height, which indicates the length of the blockchain, increases after the addition of the new block.",
hash: "The hash of the block header of the current block.", hash: "The hash of the block header of the current block.",

@ -311,7 +311,6 @@ function BlockLatency(params: { latency: number; latencyPerBlock: number[] }) {
} }
/> />
} }
plain
> >
<Text size="small" weight="bold"> <Text size="small" weight="bold">
{params.latency.toFixed(2)}s {params.latency.toFixed(2)}s

@ -22,7 +22,7 @@ function getColumns(props: any) {
size: "xxsmall", size: "xxsmall",
resizeable: false, resizeable: false,
header: ( header: (
<Text color="minorText" size="small" style={{ fontWeight: 300 }}> <Text color="minorText" size="small">
Shard Shard
</Text> </Text>
), ),
@ -43,7 +43,7 @@ function getColumns(props: any) {
size: "xsmall", size: "xsmall",
resizeable: false, resizeable: false,
header: ( header: (
<Text color="minorText" size="small" style={{ fontWeight: 300 }}> <Text color="minorText" size="small">
Hash Hash
</Text> </Text>
), ),
@ -65,7 +65,7 @@ function getColumns(props: any) {
size: "260px", size: "260px",
resizeable: false, resizeable: false,
header: ( header: (
<Text color="minorText" size="small" style={{ fontWeight: 300 }}> <Text color="minorText" size="small">
Block number Block number
</Text> </Text>
), ),
@ -89,7 +89,7 @@ function getColumns(props: any) {
size: "large", size: "large",
resizeable: false, resizeable: false,
header: ( header: (
<Text color="minorText" size="small" style={{ fontWeight: 300 }}> <Text color="minorText" size="small">
From From
</Text> </Text>
), ),
@ -100,7 +100,7 @@ function getColumns(props: any) {
size: "large", size: "large",
resizeable: false, resizeable: false,
header: ( header: (
<Text color="minorText" size="small" style={{ fontWeight: 300 }}> <Text color="minorText" size="small">
To To
</Text> </Text>
), ),
@ -111,7 +111,7 @@ function getColumns(props: any) {
size: "380px", size: "380px",
resizeable: false, resizeable: false,
header: ( header: (
<Text color="minorText" size="small" style={{ fontWeight: 300 }}> <Text color="minorText" size="small">
ONEValue ONEValue
</Text> </Text>
), ),
@ -125,7 +125,7 @@ function getColumns(props: any) {
property: "timestamp", property: "timestamp",
resizeable: false, resizeable: false,
header: ( header: (
<Text color="minorText" size="small" style={{ fontWeight: 300, width: '180px' }}> <Text color="minorText" size="small" style={{ width: '180px' }}>
Timestamp Timestamp
</Text> </Text>
), ),
@ -195,8 +195,7 @@ export function TransactionsTable(props: TransactionTableProps) {
direction="row" direction="row"
justify={hidePagination ? "start" : "between"} justify={hidePagination ? "start" : "between"}
pad={{ bottom: "small" }} pad={{ bottom: "small" }}
margin={{ bottom: "small" }} margin={{ bottom: "0" }}
border={{ size: "xsmall", side: "bottom", color: "border" }}
> >
{!hideCounter ? ( {!hideCounter ? (
<Text style={{ flex: "1 1 100%" }}> <Text style={{ flex: "1 1 100%" }}>
@ -239,15 +238,22 @@ export function TransactionsTable(props: TransactionTableProps) {
<TableComponent <TableComponent
alwaysOpenedRowDetails={props.rowDetails ? true : false} alwaysOpenedRowDetails={props.rowDetails ? true : false}
tableProps={{ tableProps={{
className: "g-table-header", className: "g-table-transactions",
style: { width: "100%", minWidth, tableLayout: 'auto' }, style: { width: "100%", minWidth, tableLayout: 'auto' },
columns: columns ? columns : getColumns({ history }), columns: columns ? columns : getColumns({ history }),
data: data, data: data,
step, step,
primaryKey: props.primaryKey ? props.primaryKey : undefined, primaryKey: props.primaryKey ? props.primaryKey : undefined,
background: {
header: {
color: 'tableRowHover'
}
},
border: { border: {
header: { header: {
color: "brand", color: "border",
side: 'top',
size: '1px'
}, },
body: { body: {
color: "border", color: "border",

@ -33,12 +33,13 @@ const getColumns = ({ type = "" }) => [
content={ content={
<TipContent <TipContent
message={ message={
transactionPropertyDescriptions[e.key + type] || <Text size={'small'}>
transactionPropertyDescriptions[e.key] {transactionPropertyDescriptions[e.key + type] ||
transactionPropertyDescriptions[e.key]}
</Text>
} }
/> />
} }
plain
> >
<span> <span>
<CircleQuestion size="small" /> <CircleQuestion size="small" />
@ -187,12 +188,10 @@ export const TransactionDetails: FunctionComponent<TransactionDetailsProps> = ({
data={txData} data={txData}
step={10} step={10}
border={{ border={{
header: { header: false,
color: "none",
},
body: { body: {
color: "border", color: "border",
side: "top", side: "bottom",
size: "1px", size: "1px",
}, },
}} }}

@ -91,6 +91,7 @@ export const transactionPropertySort: Record<string, number> = {
}; };
export const transactionPropertyDescriptions: Record<string, string> = { export const transactionPropertyDescriptions: Record<string, string> = {
Status: "The status of the transaction",
shardID: "The shard number where the transaction belongs.", shardID: "The shard number where the transaction belongs.",
blockNumber: "The number of the block in which the transaction was recorded.", blockNumber: "The number of the block in which the transaction was recorded.",
hash: "A TxHash or transaction hash is a unique 66 characters identifier that is generated whenever a transaction is executed.", hash: "A TxHash or transaction hash is a unique 66 characters identifier that is generated whenever a transaction is executed.",

@ -27,6 +27,7 @@ interface IAddress {
displayHash?: boolean; displayHash?: boolean;
noHistoryPush?: boolean; noHistoryPush?: boolean;
hideCopyBtn?: boolean; hideCopyBtn?: boolean;
showLink?: boolean;
} }
const AddressText = styled(Text)<{ isShortEllipsis?: boolean }>` const AddressText = styled(Text)<{ isShortEllipsis?: boolean }>`
@ -48,6 +49,7 @@ export const Address = (props: IAddress) => {
color = "brand", color = "brand",
displayHash, displayHash,
hideCopyBtn = false, hideCopyBtn = false,
showLink = true
} = props; } = props;
const history = useHistory(); const history = useHistory();
const ERC20Map = useERC20Pool(); const ERC20Map = useERC20Pool();
@ -81,13 +83,39 @@ export const Address = (props: IAddress) => {
parsedName = address === EMPTY_ADDRESS ? "0x0" : parsedName; parsedName = address === EMPTY_ADDRESS ? "0x0" : parsedName;
let outPutAddress = address; let outPutAddress: string;
try { try {
outPutAddress = currency === "ONE" ? getAddress(address).bech32 : toChecksumAddress(address); outPutAddress = currency === "ONE" ? getAddress(address).bech32 : toChecksumAddress(address);
} catch { } catch {
outPutAddress = address; outPutAddress = address;
} }
const addressContent = <AddressText
size="small"
color={color}
style={{
marginLeft: hideCopyBtn ? "0px" : "7px",
cursor: showLink ? 'pointer' : 'default',
...style,
}}
isShortEllipsis={isShortEllipsis}
onClick={
address === EMPTY_ADDRESS
? undefined
: props.noHistoryPush
? undefined
: (e) => {
e.preventDefault();
history.push(`/${type}/${address}`);
}
}
>
{parsedName ||
(isShort
? `${outPutAddress.substr(0, 4)}...${outPutAddress.substr(-4)}`
: outPutAddress)}
</AddressText>
return ( return (
<div style={{ display: "inline-block" }}> <div style={{ display: "inline-block" }}>
<Box direction={"row"} align={"center"} justify={"start"}> <Box direction={"row"} align={"center"} justify={"start"}>
@ -108,39 +136,14 @@ export const Address = (props: IAddress) => {
}} }}
/> />
)} )}
<Link to={address === EMPTY_ADDRESS ? "" : `/${type}/${address}`}> {showLink &&
<AddressText <Link to={address === EMPTY_ADDRESS ? "" : `/${type}/${address}`}>
size="small" {addressContent}
color={color} </Link>
style={{ }
marginLeft: hideCopyBtn ? "0px" : "7px", {!showLink &&
cursor: "pointer", addressContent
textDecoration: }
address === EMPTY_ADDRESS
? "none"
: !!parsedName
? "underline"
: "none",
...style,
}}
isShortEllipsis={isShortEllipsis}
onClick={
address === EMPTY_ADDRESS
? undefined
: props.noHistoryPush
? undefined
: (e) => {
e.preventDefault();
history.push(`/${type}/${address}`);
}
}
>
{parsedName ||
(isShort
? `${outPutAddress.substr(0, 4)}...${outPutAddress.substr(-4)}`
: outPutAddress)}
</AddressText>
</Link>
</Box> </Box>
</div> </div>
); );

@ -56,25 +56,25 @@ export const ONEValue = (props: ONEValueProps) => {
</Text> </Text>
{USDValue && +price > 0 && !isTodayTransaction && !hideTip && ( {USDValue && +price > 0 && !isTodayTransaction && !hideTip && (
<Tip <Tip
dropProps={{ align: { left: "right" }, margin: { left: "small" } }} dropProps={{ align: { bottom: "top" }}}
content={ content={
<TipContent <TipContent
showArrow={true}
message={ message={
<span> <Text size={'small'}>
{`Displaying value on ${dayjs(timestamp).format( {`Displaying value on ${dayjs(timestamp).format(
"YYYY-MM-DD" "YYYY-MM-DD"
)}. Current value`}{" "} )}. Current value:`}{" "}
<b> <b>
$ $
{formatNumber(v * +lastPrice, { {formatNumber(v * +lastPrice, {
maximumFractionDigits: 2, maximumFractionDigits: 2,
})} })}
</b> </b>
</span> </Text>
} }
/> />
} }
plain
> >
<Text size="small">(${USDValue})</Text> <Text size="small">(${USDValue})</Text>
</Tip> </Tip>

@ -173,6 +173,8 @@ export function PaginationRecordsPerPage(props: ElementsPerPage) {
onChange(newFilter); onChange(newFilter);
}; };
const renderOption = (option: string) => <Box pad={'small'}><Text size={'small'}>{option}</Text></Box>
return ( return (
<Box direction="row" gap="small" align="center"> <Box direction="row" gap="small" align="center">
<Box style={{ width: "105px" }}> <Box style={{ width: "105px" }}>
@ -180,7 +182,7 @@ export function PaginationRecordsPerPage(props: ElementsPerPage) {
options={options} options={options}
value={limit.toString()} value={limit.toString()}
onChange={onChangeLimit} onChange={onChangeLimit}
/> >{renderOption}</Select>
</Box> </Box>
<Text size="small">records per page</Text> <Text size="small">records per page</Text>
</Box> </Box>

@ -1,13 +1,34 @@
import React from 'react' import React from 'react'
import { Box, Text } from 'grommet'
import styled from "styled-components";
import { grommet, Box, Button, Grommet, Heading, Text, Tip } from 'grommet' const ArrowDown = styled(Box)`
import { Trash } from 'grommet-icons' position: absolute;
top: 100%;
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid ${(props) => props.theme.global.colors.backgroundTip};
border-bottom: 0 solid transparent;
`
// @ts-ignore export const TipContent = (props: { message: string | JSX.Element, showArrow?: boolean }) => {
export const TipContent = ({ message }) => ( let message = props.message
<Box direction="row" align="center"> if (typeof message === 'string') {
<Box background="background" direction="row" pad="small" border={{color: 'border', size: '0px' }}> message = <Text size={'small'}>{message}</Text>
<div>{message}</div> }
</Box> return <Box
direction="column"
align="center"
background="backgroundTip"
pad={{ top: 'xxsmall', left: 'small', right: 'small', bottom: 'xxsmall' }}
round={{ size: 'xsmall' }}
style={{ position: 'relative', color: 'white', width: 'fit-content' }}
>
<Box>{message}</Box>
{props.showArrow &&
<ArrowDown border={{ color: '#EFF8FF' }} />
}
</Box> </Box>
) }

@ -29,12 +29,25 @@ a {
background: transparent !important; background: transparent !important;
} }
/* Tooltip override */
body.light > div > div[aria-hidden=false] > div > div { body.light > div > div[aria-hidden=false] > div > div {
background: #fff !important; box-shadow: none;
border: 1px solid #f3f3f3; background: unset;
} }
body.dark > div > div[aria-hidden=false] > div > div { body.dark > div > div[aria-hidden=false] > div > div {
background: #1b2a5e !important; box-shadow: none;
border: 1px solid #3a54b3; background: unset;
} }
table:not(.g-table-transactions) tr:hover {
background: none;
}
table:not(.g-table-transactions) thead tr:first-child th, table:not(.g-table-transactions) thead tr:first-child td {
border: none;
}
.g-table-transactions thead tr th {
font-weight: 700;
}

@ -262,15 +262,15 @@ const addressPropertyDisplayValues: Record<
formatNumber formatNumber
/> />
<Tip <Tip
dropProps={{ align: { left: "right" } }} dropProps={{ align: { bottom: "top" }}}
content={ content={
<TipContent <TipContent
message={`last update block height ${formatNumber( showArrow={true}
message={`Last update block height: ${formatNumber(
+data.lastUpdateBlockNumber +data.lastUpdateBlockNumber
)}`} )}`}
/> />
} }
plain
> >
<span style={{ marginLeft: "5px" }}> <span style={{ marginLeft: "5px" }}>
<CircleQuestion size="small" /> <CircleQuestion size="small" />
@ -283,15 +283,15 @@ const addressPropertyDisplayValues: Record<
<Box direction={"row"}> <Box direction={"row"}>
<>{formatNumber(+value)}</> <>{formatNumber(+value)}</>
<Tip <Tip
dropProps={{ align: { left: "right" } }} dropProps={{ align: { bottom: "top" }}}
content={ content={
<TipContent <TipContent
message={`last update block height ${formatNumber( showArrow={true}
message={`Last update block height: ${formatNumber(
+data.lastUpdateBlockNumber +data.lastUpdateBlockNumber
)}`} )}`}
/> />
} }
plain
> >
<span style={{ marginLeft: "5px" }}> <span style={{ marginLeft: "5px" }}>
<CircleQuestion size="small" /> <CircleQuestion size="small" />
@ -312,15 +312,15 @@ const addressPropertyDisplayValues: Record<
formatNumber formatNumber
/> />
<Tip <Tip
dropProps={{ align: { left: "right" } }} dropProps={{ align: { bottom: "top" }}}
content={ content={
<TipContent <TipContent
message={`last update block height ${formatNumber( showArrow={true}
message={`Last update block height: ${formatNumber(
+data.lastUpdateBlockNumber +data.lastUpdateBlockNumber
)}`} )}`}
/> />
} }
plain
> >
<span style={{ marginLeft: "5px" }}> <span style={{ marginLeft: "5px" }}>
<CircleQuestion size="small" /> <CircleQuestion size="small" />

@ -179,7 +179,6 @@ export function TokensInfo(props: { value: Token[] }) {
<Tip <Tip
dropProps={{ align: { left: "right" } }} dropProps={{ align: { left: "right" } }}
content={<TipContent message={"Outdated"} />} content={<TipContent message={"Outdated"} />}
plain
> >
<span> <span>
<Alert size="small" /> <Alert size="small" />

@ -16,7 +16,7 @@ import { useHistory, useParams } from "react-router-dom";
import { useERC20Pool } from "src/hooks/ERC20_Pool"; import { useERC20Pool } from "src/hooks/ERC20_Pool";
import { useERC721Pool } from "src/hooks/ERC721_Pool"; import { useERC721Pool } from "src/hooks/ERC721_Pool";
import { useERC1155Pool } from "src/hooks/ERC1155_Pool"; import { useERC1155Pool } from "src/hooks/ERC1155_Pool";
import { Transactions } from "./tabs/Transactions"; import { Transactions } from "./tabs/transactions/Transactions";
import { import {
IUserERC721Assets, IUserERC721Assets,
TRelatedTransaction, TRelatedTransaction,

@ -10,6 +10,7 @@ import {
} from "../../../../api/client"; } from "../../../../api/client";
import styled from "styled-components"; import styled from "styled-components";
import { DisplaySignature, parseSuggestedEvent } from "../../../../web3/parseByteCode"; import { DisplaySignature, parseSuggestedEvent } from "../../../../web3/parseByteCode";
import { TipContent } from "../../../../components/ui";
const TopicsContainer = styled.div` const TopicsContainer = styled.div`
text-align: left; text-align: left;
@ -66,12 +67,15 @@ const CenteredContainer = styled.div`
function TxsHashColumn (props: { log: LogWithSignature }) { function TxsHashColumn (props: { log: LogWithSignature }) {
const { log } = props const { log } = props
return <div style={{ width: '140px' }}> return <div style={{ width: '140px' }}>
<Tip content={'Txn Hash'}> <Tip
dropProps={{ align: { bottom: "top" }}}
content={<TipContent showArrow={true} message={'Txn Hash'} />}
>
<Link to={`/tx/${log.transactionHash}`}> <Link to={`/tx/${log.transactionHash}`}>
<LinkText>{log.transactionHash}</LinkText> <LinkText>{log.transactionHash}</LinkText>
</Link> </Link>
</Tip> </Tip>
<Tip content={'Block number'}> <Tip dropProps={{ align: { bottom: "top" }}} content={<TipContent showArrow={true} message={'Block number'} />}>
<div style={{ display: 'flex', marginTop: '4px' }}> <div style={{ display: 'flex', marginTop: '4px' }}>
#&nbsp; #&nbsp;
<Link to={`/block/${log.blockNumber}`} style={{ display: 'inline-block' }}> <Link to={`/block/${log.blockNumber}`} style={{ display: 'inline-block' }}>
@ -80,7 +84,10 @@ function TxsHashColumn (props: { log: LogWithSignature }) {
</div> </div>
</Tip> </Tip>
{log.timestamp && {log.timestamp &&
<Tip content={dayjs(log.timestamp).format('MMM-DD-YYYY hh:mm:ss a') }> <Tip
dropProps={{ align: { bottom: "top" }}}
content={<TipContent showArrow={true} message={dayjs(log.timestamp).format('MMM-DD-YYYY hh:mm:ss a')} />}
>
<div style={{ marginTop: '4px' }}>{dayjs(log.timestamp).fromNow()}</div> <div style={{ marginTop: '4px' }}>{dayjs(log.timestamp).fromNow()}</div>
</Tip> </Tip>
} }
@ -92,7 +99,7 @@ function TxMethod (props: { log: LogWithSignature }) {
return <div style={{ width: '140px' }}> return <div style={{ width: '140px' }}>
<Text size="12px"> <Text size="12px">
<Tip content={'MethodID'}> <Tip dropProps={{ align: { bottom: "top" }}} content={<TipContent showArrow={true} message={'MethodID'} />}>
<NeutralMarker background={"backgroundBack"} width={'100px'}> <NeutralMarker background={"backgroundBack"} width={'100px'}>
<TextEllipsis>{props.log.input.slice(0, 10)}</TextEllipsis> <TextEllipsis>{props.log.input.slice(0, 10)}</TextEllipsis>
</NeutralMarker> </NeutralMarker>

@ -14,14 +14,14 @@ import {
} from "src/types"; } from "src/types";
import { TRelatedTransaction } from "src/api/client.interface"; import { TRelatedTransaction } from "src/api/client.interface";
import { getAddress, mapBlockchainTxToRelated } from "src/utils"; import { getAddress, mapBlockchainTxToRelated } from "src/utils";
import { ExportToCsvButton } from "../../../components/ui/ExportToCsvButton"; import { ExportToCsvButton } from "../../../../components/ui/ExportToCsvButton";
import { import {
hmyv2_getStakingTransactionsCount, hmyv2_getStakingTransactionsHistory, hmyv2_getStakingTransactionsCount, hmyv2_getStakingTransactionsHistory,
hmyv2_getTransactionsCount, hmyv2_getTransactionsCount,
hmyv2_getTransactionsHistory hmyv2_getTransactionsHistory
} from "../../../api/rpc"; } from "../../../../api/rpc";
import { getColumns, getERC20Columns, getNFTColumns, getStackingColumns } from "./txsColumns"; import { getColumns, getERC20Columns, getNFTColumns, getStakingColumns } from "./columns";
import useQuery from "../../../hooks/useQuery"; import useQuery from "../../../../hooks/useQuery";
const internalTxsBlocksFrom = 23000000 const internalTxsBlocksFrom = 23000000
const allowedLimits = [10, 25, 50, 100] const allowedLimits = [10, 25, 50, 100]
@ -217,7 +217,7 @@ export function Transactions(props: {
switch (props.type) { switch (props.type) {
case "staking_transaction": { case "staking_transaction": {
columns = getStackingColumns(id); columns = getStakingColumns(id);
break; break;
} }
case "erc20": { case "erc20": {

@ -0,0 +1,164 @@
import { Box, ColumnConfig, Text, Tip } from "grommet";
import React from "react";
import { Address, DateTime, ONEValue, TokenValue } from "src/components/ui";
import { Log, RelatedTransaction, Topic } from "src/types";
import { parseSuggestedEvent } from "src/web3/parseByteCode";
import styled, { css } from "styled-components";
import { ABIManager, IABI } from "src/web3/ABIManager";
import ERC721ABI from "src/web3/abi/ERC721ABI.json";
import ERC1155ABI from "src/web3/abi/ERC1155ABI.json";
const erc721ABIManager = ABIManager(ERC721ABI as IABI)
const erc1155ABIManager = ABIManager(ERC1155ABI as IABI)
export const transferSignature = erc721ABIManager.getEntryByName('Transfer')!.signature
export const transferSingleSignature = erc1155ABIManager.getEntryByName('TransferSingle')!.signature
export const erc20TransferTopic =
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
export type TxDirection = 'in' | 'out' | 'self'
export const Marker = styled.div<{ direction: TxDirection }>`
border-radius: 4px;
padding: 6px 3px;
width: 32px;
text-align: center;
font-weight: bold;
font-size: 90%;
${(props) =>
props.direction === 'self'
? css`
background: ${(props) => props.theme.global.colors.backgroundBack};
`
: props.direction === 'out'
? css`
color: ${(props) => props.theme.global.colors.warning};
background: ${(props) => props.theme.global.colors.warningBackground};
`
: css`
color: ${(props) => props.theme.global.colors.success};
background: ${(props) => props.theme.global.colors.successBackground}
`};
`
export const TxMethod = styled(Text)`
width: 100px;
display: block;
border-radius: 4px;
padding: 2px 4px;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
background: ${(props) => props.theme.global.colors.backgroundMark};
font-size: 12px;
`
export const TextEllipsis = styled(Text)`
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`
export const memo = (f: Function) => {
const cache = new Map()
return (data: any) => {
const hash: string = data.hash + (data.logs ? data.logs.length : '')
if (cache.has(hash)) {
return cache.get(hash)
}
const res = f(data)
const { parsed } = res
if (!parsed) {
return res
}
cache.set(hash, res)
return res
}
}
// take only first related at the moment
export const extractTransfer = memo((data: any) => {
const { relatedAddress } = data
const transferLogs = data.logs ? data.logs
.filter((d: any) => d.topics.includes(erc20TransferTopic)) : []
for (let i = 0; i < transferLogs.length; i++) {
const transferLog = transferLogs[i]
const event = parseSuggestedEvent('Transfer(address,address,uint256)', transferLog.data, transferLog.topics) || null
if (!event) {
continue
}
event.parsed['$0'] = event.parsed['$0'].toLowerCase()
event.parsed['$1'] = event.parsed['$1'].toLowerCase()
if (relatedAddress === event.parsed['$0'] || relatedAddress === event.parsed['$1']) {
return {
transferLog: transferLog || {},
parsed: event.parsed || {}
}
}
}
return {
transferLog: {},
parsed: {}
}
})
export const extractTokenId = memo((data: any) => {
const { transactionType, logs = [] } = data
const eventType = transactionType === 'erc721' ? 'Transfer' : 'TransferSingle'
const signature = transactionType === 'erc721' ? transferSignature : transferSingleSignature
const abi = transactionType === 'erc721' ? erc721ABIManager : erc1155ABIManager
const transferLogs = logs.filter(({ topics }: { topics: Topic[] }) => topics.includes(signature))
if (transferLogs.length > 0) {
try {
const log = transferLogs[0]
const [topic0, ...topics] = log.topics
const {tokenId, id} = abi.decodeLog(eventType, log.data, topics)
if (tokenId || id) {
return tokenId || id
}
} catch (e) {
console.error('Cannot decode log', (e as Error).message)
return ''
}
}
return ''
})
export const TransactionAddress = (props: { id: string, address: string, width?: string }) => {
const { id: rootAddress, address, width = '120px' } = props
const isRootAddress = rootAddress === address
return <Text size="12px">
<Address
isShortEllipsis={true}
address={props.address}
color={isRootAddress ? 'text' : 'brand'}
showLink={!isRootAddress}
style={{ width }}
/>
</Text>
}
export const TransferDirectionMarker = (props: { id: string, data: RelatedTransaction }) => {
const { id, data: { from, to } } = props
let direction: TxDirection = from === id ? 'out' : 'in'
if (from === to) {
direction = 'self'
}
return <Text size="12px">
<Marker direction={direction}>{direction.toUpperCase()}</Marker>
</Text>
}

@ -0,0 +1,141 @@
import { Box, ColumnConfig, Text } from "grommet";
import { Address, DateTime, TokenValue } from "../../../../../components/ui";
import { RelatedTransaction } from "../../../../../types";
import React from "react";
import { TransactionAddress, TransferDirectionMarker, TxMethod } from "./common";
export function getERC20Columns(id: string): ColumnConfig<any>[] {
return [
{
property: 'hash',
header: (
<Text
color="minorText"
size="small"
style={{ width: '95px' }}
>
Hash
</Text>
),
render: (data: any) => (
<Address address={data.transactionHash || data.hash} type="tx" isShortEllipsis={true} style={{ width: '170px' }} />
)
},
{
property: 'event',
header: (
<Text color="minorText" size="small">
Event
</Text>
),
render: (data: any) => {
const eventType = data.eventType || '-'
return (
<TxMethod>{eventType}</TxMethod>
)
}
},
{
property: 'from',
header: (
<Text
color="minorText"
size="small"
style={{ width: '120px' }}
>
From
</Text>
),
render: (data: RelatedTransaction) => <TransactionAddress id={id} address={data.from} />
},
{
property: 'marker',
header: <></>,
render: (data: RelatedTransaction) => <TransferDirectionMarker id={id} data={data} />
},
{
property: 'to',
header: (
<Text
color="minorText"
size="small"
style={{ width: '120px' }}
>
To
</Text>
),
render: (data: RelatedTransaction) => <TransactionAddress id={id} address={data.to} />
},
{
property: 'value',
header: (
<Text
color="minorText"
size="small"
style={{ width: '320px' }}
>
Value
</Text>
),
render: (data: any) => {
const { address, value, eventType } = data
if (!value) {
return '?'
}
if (eventType === 'Approval') {
return (
<Box direction={'row'} gap={'4px'}>
<TokenValue isShort={true} tokenAddress={address} value={value} />
</Box>)
}
return (
<Text size="12px">
<TokenValue tokenAddress={address} value={value} />
</Text>)
}
},
{
property: 'token',
header: (
<Text
color="minorText"
size="small"
style={{ width: '120px' }}
>
Token
</Text>
),
render: (data: any) => {
const address = data.address ? data.address : '—'
return (
<Text size="12px">
<Address address={address} />
</Text>
)
}
},
{
property: 'timestamp',
header: (
<Text
color="minorText"
size="small"
style={{ width: '140px' }}
>
Timestamp
</Text>
),
render: (data: RelatedTransaction) => (
<Box direction="row" gap="xsmall" justify="end">
<DateTime
date={data.timestamp}
/>
</Box>
)
}
]
}

@ -0,0 +1,11 @@
import { getColumns } from "./transactions";
import { getStakingColumns } from './staking'
import { getERC20Columns } from './erc20'
import { getNFTColumns } from './nft'
export {
getColumns,
getStakingColumns,
getERC20Columns,
getNFTColumns,
}

@ -0,0 +1,160 @@
import { Box, ColumnConfig, Text, Tip } from "grommet";
import React from "react";
import { RelatedTransaction } from "../../../../../types";
import { Address, DateTime } from "../../../../../components/ui";
import {
extractTokenId,
TextEllipsis,
TransactionAddress,
TransferDirectionMarker,
TxMethod
} from "./common";
export function getNFTColumns(id: string): ColumnConfig<any>[] {
return [
{
property: "hash",
header: (
<Text
color="minorText"
size="small"
style={{ width: "95px" }}
>
Hash
</Text>
),
render: (data: any) => (
<Address
address={data.transactionHash || data.hash}
type="tx"
isShortEllipsis={true}
style={{ width: "170px" }}
/>
),
},
{
property: 'event',
header: (
<Text color="minorText" size="small">
Event
</Text>
),
render: (data: any) => {
const eventType = data.eventType || '-'
return (
<TxMethod>{eventType}</TxMethod>
)
}
},
{
property: "from",
header: (
<Text
color="minorText"
size="small"
style={{ width: "180px" }}
>
From
</Text>
),
render: (data: RelatedTransaction) => <TransactionAddress id={id} address={data.from} width={'180px'} />,
},
{
property: "marker",
header: <></>,
render: (data: RelatedTransaction) => <TransferDirectionMarker id={id} data={data} />,
},
{
property: "to",
header: (
<Text
color="minorText"
size="small"
style={{ width: "180px" }}
>
To
</Text>
),
render: (data: RelatedTransaction) => data.to.trim() && <TransactionAddress id={id} address={data.to} width={'180px'} />,
},
// {
// property: "value",
// header: (
// <Text
// color="minorText"
// size="small"
// style={{ width: "120px" }}
// >
// Value
// </Text>
// ),
// render: (data: RelatedTransaction) => (
// <Box justify="center">
// <ONEValue value={data.value} timestamp={data.timestamp} />
// </Box>
// ),
// },
{
property: 'tokenId',
header: (
<Text
color="minorText"
size="small"
style={{ width: '120px' }}
>
TokenId
</Text>
),
render: (data: any) => {
const tokenId = extractTokenId(data)
return (
<Tip content={tokenId}>
<TextEllipsis size="12px" style={{ width: '115px' }}>
{tokenId}
</TextEllipsis>
</Tip>
)
}
},
{
property: 'token',
header: (
<Text
color="minorText"
size="small"
style={{ width: '120px' }}
>
Token
</Text>
),
render: (data: any) => {
const address = data.address ? data.address : '—'
return (
<Text size="12px">
<Address address={address} />
</Text>
)
}
},
{
property: "timestamp",
header: (
<Text
color="minorText"
size="small"
style={{ width: "140px" }}
>
Timestamp
</Text>
),
render: (data: RelatedTransaction) => (
<Box direction="row" gap="xsmall" justify="end">
<DateTime
date={data.timestamp}
/>
</Box>
),
},
];
}

@ -0,0 +1,122 @@
import { Box, ColumnConfig, Text } from "grommet";
import React from "react";
import { RelatedTransaction } from "../../../../../types";
import { Address, DateTime, ONEValue } from "../../../../../components/ui";
import { TransactionAddress, TransferDirectionMarker } from "./common";
export const getStakingColumns = (id: string): ColumnConfig<any>[] => {
return [
{
property: "hash",
header: (
<Text
color="minorText"
size="small"
style={{ width: "95px" }}
>
Hash
</Text>
),
render: (data: any) => (
<Address
address={data.transactionHash || data.hash}
type="staking-tx"
isShortEllipsis={true}
style={{ width: "170px" }}
/>
),
},
{
property: "type",
header: (
<Text
color="minorText"
size="small"
style={{ width: "140px" }}
>
Type
</Text>
),
render: (data: RelatedTransaction) => (
<Text size="small" style={{ width: "140px" }}>
{data.type}
</Text>
),
},
{
property: "validator",
header: (
<Text
color="minorText"
size="small"
style={{ width: "170px" }}
>
Validator
</Text>
),
render: (data: RelatedTransaction) => data.msg?.validatorAddress
? <TransactionAddress id={id} address={data.msg?.validatorAddress || data.from} width={'170px'} />
: '—'
},
{
property: "marker",
header: <></>,
render: (data: RelatedTransaction) => <TransferDirectionMarker id={id} data={data} />,
},
{
property: "delegator",
header: (
<Text
color="minorText"
size="small"
style={{ width: "170px" }}
>
Delegator
</Text>
),
render: (data: RelatedTransaction) => data.msg?.delegatorAddress
? <TransactionAddress id={id} address={data.msg?.delegatorAddress} width={'170px'} />
: '—',
},
{
property: "value",
header: (
<Text
color="minorText"
size="small"
style={{ width: "220px" }}
>
Value
</Text>
),
render: (data: RelatedTransaction) => (
<Box justify="center">
{data.msg?.amount ? (
<ONEValue value={data.msg?.amount} timestamp={data.timestamp} />
) : data.amount ? (
<ONEValue value={data.amount} timestamp={data.timestamp} />
) : (
"—"
)}
</Box>
),
},
{
property: "timestamp",
header: (
<Text
color="minorText"
size="small"
style={{ width: "140px" }}
>
Timestamp
</Text>
),
render: (data: RelatedTransaction) => (
<Box direction="row" gap="xsmall" justify="end">
<DateTime date={data.timestamp} />
</Box>
),
},
];
};

@ -0,0 +1,177 @@
import { Box, ColumnConfig, Text, Tip } from "grommet";
import React from "react";
import { RelatedTransaction } from "../../../../../types";
import { Address, DateTime, ONEValue, TipContent } from "../../../../../components/ui";
import { TransactionAddress, TransferDirectionMarker, TxMethod } from "./common";
export function getColumns(id: string): ColumnConfig<any>[] {
return [
// {
// property: "type",
// size: "",
// header: (
// <Text
// color="minorText"
// size="small"
// style={{ width: "140px" }}
// >
// Type
// </Text>
// ),
// render: (data: RelatedTransaction) => (
// <Text size="small" style={{ width: "140px" }}>
// {relatedTxMap[data.transactionType] || data.transactionType}
// </Text>
// ),
// },
{
property: "hash",
header: (
<Text
color="minorText"
size="small"
style={{ width: "95px" }}
>
Hash
</Text>
),
render: (data: any) => (
<Address
address={data.transactionHash || data.hash}
type="tx"
isShortEllipsis={true}
style={{ width: "170px" }}
/>
),
},
{
property: "method",
header: (
<Text color="minorText" size="small">
Method
</Text>
),
render: (data: any) => {
let signature;
try {
// @ts-ignore
signature =
data.signatures &&
data.signatures.map((s: any) => s.signature)[0].split("(")[0];
} catch (err) {}
if (!signature && data.value !== "0") {
signature = "transfer";
}
if (!signature && data.input.length >= 10) {
signature = data.input.slice(2, 10);
}
if (!signature) {
return <Text size="small">{"—"}</Text>;
}
const tipContent = <TipContent
showArrow={true}
message={<Text size={'small'} textAlign={'center'}>{signature}</Text>}
/>
return (
<Tip
dropProps={{ align: { bottom: "top" }}}
content={tipContent}
>
<TxMethod>{signature}</TxMethod>
</Tip>
);
},
},
// {
// property: "shard",
// header: (
// <Text color="minorText" size="small">
// Shard
// </Text>
// ),
// render: (data: RelatedTransaction) => (
// <Box direction="row" gap="3px" align="center">
// <Text size="small">{0}</Text>
// <FormNextLink
// size="small"
// color="brand"
// style={{ marginBottom: "2px" }}
// />
// <Text size="small">{0}</Text>
// </Box>
// ),
// },
{
property: "from",
header: (
<Text
color="minorText"
size="small"
style={{ width: "180px" }}
>
From
</Text>
),
render: (data: RelatedTransaction) => <TransactionAddress id={id} address={data.from} width={'180px'} />,
},
{
property: "marker",
header: <></>,
render: (data: RelatedTransaction) => <TransferDirectionMarker id={id} data={data} />,
},
{
property: "to",
header: (
<Text
color="minorText"
size="small"
style={{ width: "180px" }}
>
To
</Text>
),
render: (data: RelatedTransaction) => <TransactionAddress id={id} address={data.to} width={'180px'} />,
},
{
property: "value",
header: (
<Text
color="minorText"
size="small"
style={{ width: "120px" }}
>
Value
</Text>
),
render: (data: RelatedTransaction) => (
<Box justify="center">
<ONEValue value={data.value} timestamp={data.timestamp} />
</Box>
),
},
{
property: "timestamp",
header: (
<Text
color="minorText"
size="small"
style={{ width: "140px" }}
>
Timestamp
</Text>
),
render: (data: RelatedTransaction) => (
<Box direction="row" gap="xsmall" justify="end">
<DateTime date={data.timestamp} />
</Box>
),
},
];
}

@ -1,778 +0,0 @@
import { Box, ColumnConfig, Text, Tip } from "grommet";
import { Address, DateTime, ONEValue, TokenValue } from "../../../components/ui";
import { Log, RelatedTransaction, Topic } from "../../../types";
import React from "react";
import { parseSuggestedEvent } from "../../../web3/parseByteCode";
import styled, { css } from "styled-components";
import { ABIManager, IABI } from "../../../web3/ABIManager";
import ERC721ABI from "../../../web3/abi/ERC721ABI.json";
import ERC1155ABI from "../../../web3/abi/ERC1155ABI.json";
const erc721ABIManager = ABIManager(ERC721ABI as IABI)
const erc1155ABIManager = ABIManager(ERC1155ABI as IABI)
const transferSignature = erc721ABIManager.getEntryByName('Transfer')!.signature
const transferSingleSignature = erc1155ABIManager.getEntryByName('TransferSingle')!.signature
const erc20TransferTopic =
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
type TxDirection = 'in' | 'out' | 'self'
const Marker = styled.div<{ direction: TxDirection }>`
border-radius: 2px;
padding: 5px;
text-align: center;
font-weight: bold;
${(props) =>
props.direction === 'self'
? css`
background: ${(props) => props.theme.global.colors.backgroundBack};
`
: props.direction === 'out'
? css`
background: rgb(239 145 62);
color: #fff;
`
: css`
background: rgba(105, 250, 189, 0.8);
color: #1b295e;
`};
`
const NeutralMarker = styled(Box)`
border-radius: 2px;
padding: 5px;
text-align: center;
`
const TxMethod = styled(Text)`
width: 100px;
> div {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
`
const TextEllipsis = styled(Text)`
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`
const memo = (f: Function) => {
const cache = new Map()
return (data: any) => {
const hash: string = data.hash + (data.logs ? data.logs.length : '')
if (cache.has(hash)) {
return cache.get(hash)
}
const res = f(data)
const { parsed } = res
if (!parsed) {
return res
}
cache.set(hash, res)
return res
}
}
// take only first related at the moment
const extractTransfer = memo((data: any) => {
const { relatedAddress } = data
const transferLogs = data.logs ? data.logs
.filter((d: any) => d.topics.includes(erc20TransferTopic)) : []
for (let i = 0; i < transferLogs.length; i++) {
const transferLog = transferLogs[i]
const event = parseSuggestedEvent('Transfer(address,address,uint256)', transferLog.data, transferLog.topics) || null
if (!event) {
continue
}
event.parsed['$0'] = event.parsed['$0'].toLowerCase()
event.parsed['$1'] = event.parsed['$1'].toLowerCase()
if (relatedAddress === event.parsed['$0'] || relatedAddress === event.parsed['$1']) {
return {
transferLog: transferLog || {},
parsed: event.parsed || {}
}
}
}
return {
transferLog: {},
parsed: {}
}
})
const extractTokenId = memo((data: any) => {
const { transactionType, logs = [] } = data
const eventType = transactionType === 'erc721' ? 'Transfer' : 'TransferSingle'
const signature = transactionType === 'erc721' ? transferSignature : transferSingleSignature
const abi = transactionType === 'erc721' ? erc721ABIManager : erc1155ABIManager
const transferLogs = logs.filter(({ topics }: { topics: Topic[] }) => topics.includes(signature))
if (transferLogs.length > 0) {
try {
const log = transferLogs[0]
const [topic0, ...topics] = log.topics
const {tokenId, id} = abi.decodeLog(eventType, log.data, topics)
if (tokenId || id) {
return tokenId || id
}
} catch (e) {
console.error('Cannot decode log', (e as Error).message)
return ''
}
}
return ''
})
const TransferDirectionMarker = (props: { id: string, data: RelatedTransaction }) => {
const { id, data: { from, to } } = props
let direction: TxDirection = from === id ? 'out' : 'in'
if (from === to) {
direction = 'self'
}
return <Text size="12px">
<Marker direction={direction}>{direction.toUpperCase()}</Marker>
</Text>
}
export function getERC20Columns(id: string): ColumnConfig<any>[] {
return [
{
property: 'hash',
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: '95px' }}
>
Hash
</Text>
),
render: (data: any) => (
<Address address={data.transactionHash || data.hash} type="tx" isShortEllipsis={true} style={{ width: '170px' }} />
)
},
{
property: 'event',
header: (
<Text color="minorText" size="small" style={{ fontWeight: 300 }}>
Event
</Text>
),
render: (data: any) => {
const eventType = data.eventType || '-'
return (
<TxMethod size="10px">
<NeutralMarker background={'backgroundBack'}>
{eventType}
</NeutralMarker>
</TxMethod>
)
}
},
{
property: 'from',
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: '120px' }}
>
From
</Text>
),
render: (data: RelatedTransaction) => {
const { from } = data
return (
<Text size="12px">
<Address isShortEllipsis={true} address={from} style={{ width: '120px' }} />
</Text>)
}
},
{
property: 'marker',
header: <></>,
render: (data: RelatedTransaction) => <TransferDirectionMarker id={id} data={data} />
},
{
property: 'to',
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: '120px' }}
>
To
</Text>
),
render: (data: RelatedTransaction) => {
const { to } = data
return (
<Text size="12px">
<Address isShortEllipsis={true} address={to} style={{ width: '120px' }} />
</Text>)
}
},
{
property: 'value',
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: '320px' }}
>
Value
</Text>
),
render: (data: any) => {
const { address, value, eventType } = data
if (!value) {
return '?'
}
if (eventType === 'Approval') {
return (
<Box direction={'row'} gap={'4px'}>
<TokenValue isShort={true} tokenAddress={address} value={value} />
</Box>)
}
return (
<Text size="12px">
<TokenValue tokenAddress={address} value={value} />
</Text>)
}
},
{
property: 'token',
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: '120px' }}
>
Token
</Text>
),
render: (data: any) => {
const address = data.address ? data.address : '—'
return (
<Text size="12px">
<Address address={address} />
</Text>
)
}
},
{
property: 'timestamp',
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: '140px' }}
>
Timestamp
</Text>
),
render: (data: RelatedTransaction) => (
<Box direction="row" gap="xsmall" justify="end">
<DateTime
date={data.timestamp}
/>
</Box>
)
}
]
}
export function getColumns(id: string): ColumnConfig<any>[] {
return [
// {
// property: "type",
// size: "",
// header: (
// <Text
// color="minorText"
// size="small"
// style={{ fontWeight: 300, width: "140px" }}
// >
// Type
// </Text>
// ),
// render: (data: RelatedTransaction) => (
// <Text size="small" style={{ width: "140px" }}>
// {relatedTxMap[data.transactionType] || data.transactionType}
// </Text>
// ),
// },
{
property: "hash",
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: "95px" }}
>
Hash
</Text>
),
render: (data: any) => (
<Address
address={data.transactionHash || data.hash}
type="tx"
isShortEllipsis={true}
style={{ width: "170px" }}
/>
),
},
{
property: "method",
header: (
<Text color="minorText" size="small" style={{ fontWeight: 300 }}>
Method
</Text>
),
render: (data: any) => {
let signature;
try {
// @ts-ignore
signature =
data.signatures &&
data.signatures.map((s: any) => s.signature)[0].split("(")[0];
} catch (err) {}
if (!signature && data.value !== "0") {
signature = "transfer";
}
if (!signature && data.input.length >= 10) {
signature = data.input.slice(2, 10);
}
if (!signature) {
return <Text size="small">{"—"}</Text>;
}
return (
<Tip content={<span>{signature}</span>}>
<TxMethod size="10px">
<NeutralMarker background={"backgroundBack"}>
{signature}
</NeutralMarker>
</TxMethod>
</Tip>
);
},
},
// {
// property: "shard",
// header: (
// <Text color="minorText" size="small" style={{ fontWeight: 300 }}>
// Shard
// </Text>
// ),
// render: (data: RelatedTransaction) => (
// <Box direction="row" gap="3px" align="center">
// <Text size="small">{0}</Text>
// <FormNextLink
// size="small"
// color="brand"
// style={{ marginBottom: "2px" }}
// />
// <Text size="small">{0}</Text>
// </Box>
// ),
// },
{
property: "from",
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: "180px" }}
>
From
</Text>
),
render: (data: RelatedTransaction) => (
<Text size="12px">
<Address address={data.from} isShortEllipsis={true} style={{ width: '180px' }} />
</Text>
),
},
{
property: "marker",
header: <></>,
render: (data: RelatedTransaction) => <TransferDirectionMarker id={id} data={data} />,
},
{
property: "to",
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: "180px" }}
>
To
</Text>
),
render: (data: RelatedTransaction) => (
<Text size="12px">
<Address address={data.to} isShortEllipsis={true} style={{ width: '180px' }} />
</Text>
),
},
{
property: "value",
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: "120px" }}
>
Value
</Text>
),
render: (data: RelatedTransaction) => (
<Box justify="center">
<ONEValue value={data.value} timestamp={data.timestamp} />
</Box>
),
},
{
property: "timestamp",
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: "140px" }}
>
Timestamp
</Text>
),
render: (data: RelatedTransaction) => (
<Box direction="row" gap="xsmall" justify="end">
<DateTime
date={data.timestamp}
/>
</Box>
),
},
];
}
export function getNFTColumns(id: string): ColumnConfig<any>[] {
return [
{
property: "hash",
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: "95px" }}
>
Hash
</Text>
),
render: (data: any) => (
<Address
address={data.transactionHash || data.hash}
type="tx"
isShortEllipsis={true}
style={{ width: "170px" }}
/>
),
},
{
property: 'event',
header: (
<Text color="minorText" size="small" style={{ fontWeight: 300 }}>
Event
</Text>
),
render: (data: any) => {
const eventType = data.eventType || '-'
return (
<TxMethod size="10px">
<NeutralMarker background={'backgroundBack'}>
{eventType}
</NeutralMarker>
</TxMethod>
)
}
},
{
property: "from",
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: "180px" }}
>
From
</Text>
),
render: (data: RelatedTransaction) => (
<Text size="12px">
<Address address={data.from} isShortEllipsis={true} style={{ width: '180px' }} />
</Text>
),
},
{
property: "marker",
header: <></>,
render: (data: RelatedTransaction) => <TransferDirectionMarker id={id} data={data} />,
},
{
property: "to",
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: "180px" }}
>
To
</Text>
),
render: (data: RelatedTransaction) => {
return (
<Text size="12px">
{data.to.trim() && <Address address={data.to} isShortEllipsis={true} style={{ width: '180px' }} />}
</Text>
)
},
},
// {
// property: "value",
// header: (
// <Text
// color="minorText"
// size="small"
// style={{ fontWeight: 300, width: "120px" }}
// >
// Value
// </Text>
// ),
// render: (data: RelatedTransaction) => (
// <Box justify="center">
// <ONEValue value={data.value} timestamp={data.timestamp} />
// </Box>
// ),
// },
{
property: 'tokenId',
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: '120px' }}
>
TokenId
</Text>
),
render: (data: any) => {
const tokenId = extractTokenId(data)
return (
<Tip content={tokenId}>
<TextEllipsis size="12px" style={{ width: '115px' }}>
{tokenId}
</TextEllipsis>
</Tip>
)
}
},
{
property: 'token',
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: '120px' }}
>
Token
</Text>
),
render: (data: any) => {
const address = data.address ? data.address : '—'
return (
<Text size="12px">
<Address address={address} />
</Text>
)
}
},
{
property: "timestamp",
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: "140px" }}
>
Timestamp
</Text>
),
render: (data: RelatedTransaction) => (
<Box direction="row" gap="xsmall" justify="end">
<DateTime
date={data.timestamp}
/>
</Box>
),
},
];
}
export const getStackingColumns = (id: string): ColumnConfig<any>[] => {
return [
{
property: "hash",
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: "95px" }}
>
Hash
</Text>
),
render: (data: any) => (
<Address
address={data.transactionHash || data.hash}
type="staking-tx"
isShortEllipsis={true}
style={{ width: "170px" }}
/>
),
},
{
property: "type",
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: "140px" }}
>
Type
</Text>
),
render: (data: RelatedTransaction) => (
<Text size="small" style={{ width: "140px" }}>
{data.type}
</Text>
),
},
{
property: "validator",
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: "170px" }}
>
Validator
</Text>
),
render: (data: RelatedTransaction) => (
<Text size="12px">
{data.msg?.validatorAddress ? (
<Address address={data.msg?.validatorAddress || data.from} isShortEllipsis={true} style={{ width: "170px" }} />
) : (
"—"
)}
</Text>
),
},
{
property: "marker",
header: <></>,
render: (data: RelatedTransaction) => <TransferDirectionMarker id={id} data={data} />,
},
{
property: "delegator",
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: "170px" }}
>
Delegator
</Text>
),
render: (data: RelatedTransaction) => (
<Text size="12px">
{data.msg?.delegatorAddress ? (
<Address address={data.msg?.delegatorAddress} isShortEllipsis={true} style={{ width: "170px" }} />
) : (
"—"
)}
</Text>
),
},
{
property: "value",
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: "220px" }}
>
Value
</Text>
),
render: (data: RelatedTransaction) => (
<Box justify="center">
{data.msg?.amount ? (
<ONEValue value={data.msg?.amount} timestamp={data.timestamp} />
) : data.amount ? (
<ONEValue value={data.amount} timestamp={data.timestamp} />
) : (
"—"
)}
</Box>
),
},
{
property: "timestamp",
header: (
<Text
color="minorText"
size="small"
style={{ fontWeight: 300, width: "140px" }}
>
Timestamp
</Text>
),
render: (data: RelatedTransaction) => (
<Box direction="row" gap="xsmall" justify="end">
<DateTime date={data.timestamp} />
</Box>
),
},
];
};

@ -167,14 +167,21 @@ export function AllBlocksTable() {
</Box> </Box>
<Box style={{ overflow: "auto" }}> <Box style={{ overflow: "auto" }}>
<DataTable <DataTable
className={"g-table-header"} className={"g-table-transactions"}
style={{ width: "100%", minWidth: "1110px" }} style={{ width: "100%", minWidth: "1110px" }}
columns={getColumns({ history, shardNumber })} columns={getColumns({ history, shardNumber })}
data={blocks} data={blocks}
step={10} step={10}
background={{
header: {
color: 'tableRowHover'
}
}}
border={{ border={{
header: { header: {
color: "brand", color: "border",
side: 'top',
size: '1px'
}, },
body: { body: {
color: "border", color: "border",

@ -87,15 +87,14 @@ export function ERC1155Table(props: TransactionTableProps) {
columns={getColumns({ history })} columns={getColumns({ history })}
data={data} data={data}
border={{ border={{
header: { header: false,
color: "brand",
},
body: { body: {
color: "border", color: "border",
side: "top", side: "bottom",
size: "1px", size: "1px",
}, },
}} }}
background={{header: 'unset'}}
/> />
</Box> </Box>
<Box <Box
@ -146,7 +145,7 @@ function getColumns(props: any) {
render: (data: Erc20) => <Box direction={'row'} style={{ display: 'flex', alignItems: 'center' }}> render: (data: Erc20) => <Box direction={'row'} style={{ display: 'flex', alignItems: 'center' }}>
<Text size={'small'}>{data.name}</Text> <Text size={'small'}>{data.name}</Text>
{data.isBridged && <div style={{ marginLeft: "4px", height: '14px', cursor: 'pointer' }}> {data.isBridged && <div style={{ marginLeft: "4px", height: '14px', cursor: 'pointer' }}>
<Tip content={'Token is available on Harmony Bridge'}> <Tip dropProps={{ align: { bottom: "top" }}} content={<TipContent showArrow={true} message={'Token is available on Harmony Bridge'} />}>
<StatusGood size={'14px'} color={'successText'} /> <StatusGood size={'14px'} color={'successText'} />
</Tip> </Tip>
</div>} </div>}
@ -204,12 +203,11 @@ function getColumns(props: any) {
dropProps={{ align: { right: "left" } }} dropProps={{ align: { right: "left" } }}
content={ content={
<TipContent <TipContent
message={`last update block height ${formatNumber( message={`Last update block height: ${formatNumber(
+data.lastUpdateBlockNumber +data.lastUpdateBlockNumber
)}`} )}`}
/> />
} }
plain
> >
<span style={{ marginLeft: "5px" }}> <span style={{ marginLeft: "5px" }}>
<CircleQuestion size="small" /> <CircleQuestion size="small" />

@ -87,15 +87,14 @@ export function ERC20Table(props: TransactionTableProps) {
primaryKey={'address'} primaryKey={'address'}
data={data} data={data}
border={{ border={{
header: { header: false,
color: "brand",
},
body: { body: {
color: "border", color: "border",
side: "top", side: "bottom",
size: "1px", size: "1px",
}, },
}} }}
background={{header: 'unset'}}
/> />
</Box> </Box>
<Box <Box
@ -132,7 +131,7 @@ function getColumns(props: any) {
render: (data: Erc20) => <Box direction={'row'} style={{ display: 'flex', alignItems: 'center' }}> render: (data: Erc20) => <Box direction={'row'} style={{ display: 'flex', alignItems: 'center' }}>
<Text size={'small'}>{data.name}</Text> <Text size={'small'}>{data.name}</Text>
{data.isBridged && <div style={{ marginLeft: "4px", height: '14px', cursor: 'pointer' }}> {data.isBridged && <div style={{ marginLeft: "4px", height: '14px', cursor: 'pointer' }}>
<Tip content={'Token is available on Harmony Bridge'}> <Tip dropProps={{ align: { bottom: "top" }}} content={<TipContent showArrow={true} message={'Token is available on Harmony Bridge'} />}>
<StatusGood size={'14px'} color={'successText'} /> <StatusGood size={'14px'} color={'successText'} />
</Tip> </Tip>
</div>} </div>}
@ -181,12 +180,11 @@ function getColumns(props: any) {
dropProps={{ align: { left: "right" } }} dropProps={{ align: { left: "right" } }}
content={ content={
<TipContent <TipContent
message={`last update block height ${formatNumber( message={`Last update block height: ${formatNumber(
+data.lastUpdateBlockNumber +data.lastUpdateBlockNumber
)}`} )}`}
/> />
} }
plain
> >
<span style={{ marginLeft: "5px" }}> <span style={{ marginLeft: "5px" }}>
<CircleQuestion size="small" /> <CircleQuestion size="small" />
@ -218,12 +216,11 @@ function getColumns(props: any) {
dropProps={{ align: { right: "left" } }} dropProps={{ align: { right: "left" } }}
content={ content={
<TipContent <TipContent
message={`last update block height ${formatNumber( message={`Last update block height: ${formatNumber(
+data.lastUpdateBlockNumber +data.lastUpdateBlockNumber
)}`} )}`}
/> />
} }
plain
> >
<span style={{ marginLeft: "5px" }}> <span style={{ marginLeft: "5px" }}>
<CircleQuestion size="small" /> <CircleQuestion size="small" />
@ -251,12 +248,11 @@ function getColumns(props: any) {
dropProps={{ align: { right: "left" } }} dropProps={{ align: { right: "left" } }}
content={ content={
<TipContent <TipContent
message={`last update block height ${formatNumber( message={`Last update block height: ${formatNumber(
+data.lastUpdateBlockNumber +data.lastUpdateBlockNumber
)}`} )}`}
/> />
} }
plain
> >
<span style={{ marginLeft: "5px" }}> <span style={{ marginLeft: "5px" }}>
<CircleQuestion size="small" /> <CircleQuestion size="small" />

@ -86,15 +86,14 @@ export function ERC721Table(props: TransactionTableProps) {
data={data} data={data}
primaryKey={'address'} primaryKey={'address'}
border={{ border={{
header: { header: false,
color: "brand",
},
body: { body: {
color: "border", color: "border",
side: "top", side: "bottom",
size: "1px", size: "1px",
}, },
}} }}
background={{header: 'unset'}}
/> />
</Box> </Box>
<Box <Box
@ -131,7 +130,7 @@ function getColumns(props: any) {
render: (data: Erc20) => <Box direction={'row'} style={{ display: 'flex', alignItems: 'center' }}> render: (data: Erc20) => <Box direction={'row'} style={{ display: 'flex', alignItems: 'center' }}>
<Text size={'small'}>{data.name}</Text> <Text size={'small'}>{data.name}</Text>
{data.isBridged && <div style={{ marginLeft: "4px", height: '14px', cursor: 'pointer' }}> {data.isBridged && <div style={{ marginLeft: "4px", height: '14px', cursor: 'pointer' }}>
<Tip content={'Token is available on Harmony Bridge'}> <Tip dropProps={{ align: { bottom: "top" }}} content={<TipContent showArrow={true} message={'Token is available on Harmony Bridge'} />}>
<StatusGood size={'14px'} color={'successText'} /> <StatusGood size={'14px'} color={'successText'} />
</Tip> </Tip>
</div>} </div>}
@ -191,12 +190,11 @@ function getColumns(props: any) {
dropProps={{ align: { right: "left" } }} dropProps={{ align: { right: "left" } }}
content={ content={
<TipContent <TipContent
message={`last update block height ${formatNumber( message={`Last update block height: ${formatNumber(
+data.lastUpdateBlockNumber +data.lastUpdateBlockNumber
)}`} )}`}
/> />
} }
plain
> >
<span style={{ marginLeft: "5px" }}> <span style={{ marginLeft: "5px" }}>
<CircleQuestion size="small" /> <CircleQuestion size="small" />

@ -1,6 +1,6 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { StatusCritical } from "grommet-icons"; import { StatusCritical } from "grommet-icons";
import { Address, BaseContainer, BasePage, Button } from "src/components/ui"; import { Address, BaseContainer, BasePage, Button, TipContent } from "src/components/ui";
import { Heading, DateInput, Box, Spinner, Tip, Text } from "grommet"; import { Heading, DateInput, Box, Spinner, Tip, Text } from "grommet";
import styled from "styled-components"; import styled from "styled-components";
import useQuery from "../../hooks/useQuery"; import useQuery from "../../hooks/useQuery";
@ -118,7 +118,7 @@ export const ExportData = () => {
</Box> </Box>
<FlexWrapper> <FlexWrapper>
<InputContainer> <InputContainer>
<Tip content={<span>Select start date</span>}> <Tip dropProps={{ align: { bottom: "top" }}} content={<TipContent showArrow={true} message={'Select start date'} />}>
<DateInput <DateInput
{...dateInputProps} {...dateInputProps}
value={dayjs(dateFrom).toISOString()} value={dayjs(dateFrom).toISOString()}
@ -128,7 +128,7 @@ export const ExportData = () => {
</InputContainer> </InputContainer>
<div>to</div> <div>to</div>
<InputContainer> <InputContainer>
<Tip content={<span>Select end date</span>}> <Tip dropProps={{ align: { bottom: "top" }}} content={<TipContent showArrow={true} message={'Select end date'} />}>
<DateInput <DateInput
{...dateInputProps} {...dateInputProps}
value={dayjs(dateTo).toISOString()} value={dayjs(dateTo).toISOString()}

@ -97,15 +97,14 @@ export const LatestBlocksTable = (params: { blocks: Block[] }) => {
data={params.blocks} data={params.blocks}
step={10} step={10}
border={{ border={{
header: { header: false,
color: "brand",
},
body: { body: {
color: "border", color: "border",
side: "top", side: "bottom",
size: "1px", size: "1px",
}, },
}} }}
background={{header: 'unset'}}
/> />
</Box> </Box>
); );

@ -154,21 +154,20 @@ export function LatestTransactionsTable() {
return ( return (
<Box style={{ overflow: "auto" }}> <Box style={{ overflow: "auto" }}>
<DataTable <DataTable
className={"g-table-header"} className={"g-table-header g-table-header-plain"}
style={{ width: "100%", minWidth: "620px" }} style={{ width: "100%", minWidth: "620px" }}
columns={getColumns({ history })} columns={getColumns({ history })}
data={transactions} data={transactions}
step={10} step={10}
border={{ border={{
header: { header: false,
color: "brand",
},
body: { body: {
color: "border", color: "border",
side: "top", side: "bottom",
size: "1px", size: "1px",
}, },
}} }}
background={{header: 'unset'}}
/> />
</Box> </Box>
); );

@ -1,7 +1,7 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { Box, Heading, Select, Spinner, Text, TextArea, TextInput, Tip } from "grommet"; import { Box, Heading, Select, Spinner, Text, TextArea, TextInput, Tip } from "grommet";
import { Alert, StatusGood } from "grommet-icons"; import { Alert, StatusGood } from "grommet-icons";
import { BaseContainer, BasePage } from "../../../components/ui"; import { BaseContainer, BasePage, TipContent } from "../../../components/ui";
import useQuery from "../../../hooks/useQuery"; import useQuery from "../../../hooks/useQuery";
import { getContractsByField } from "../../../api/client"; import { getContractsByField } from "../../../api/client";
import styled from "styled-components"; import styled from "styled-components";
@ -230,8 +230,18 @@ export function CheckHRC() {
{(!isContractLoading && contractAddress) && {(!isContractLoading && contractAddress) &&
<Box justify="center" align="center" margin={'24px 0 0 24px'}> <Box justify="center" align="center" margin={'24px 0 0 24px'}>
{isAllEventsMatched {isAllEventsMatched
? <Tip content={'All events found'}><StatusGood size="medium" color={'successText'} /></Tip> ? <Tip
: <Tip content={'Some events missing'}><Alert size="medium" color={'minorText'} /></Tip>} dropProps={{ align: { bottom: "top" }}}
content={<TipContent showArrow={true} message={'All events found'} />}
>
<StatusGood size="medium" color={'successText'} />
</Tip>
: <Tip
dropProps={{ align: { bottom: "top" }}}
content={<TipContent showArrow={true} message={'Some events missing'} />}
>
<Alert size="medium" color={'minorText'} />
</Tip>}
</Box> </Box>
} }
</Box> </Box>

@ -1,12 +1,18 @@
export const palette = { export const palette = {
WhiteGrey: "#F4F7F9",
LightGrey: "#e7ecf7", LightGrey: "#e7ecf7",
Grey: "#b1b1b1", Grey: "#b1b1b1",
CoolGray: "#758796", CoolGray: "#758796",
WhiteBlue: '#EFF8FF',
Purple: "#00AEE9", Purple: "#00AEE9",
ElectricBlue: "#00AEE9", ElectricBlue: "#00AEE9",
ElectricBlueLight: "#e8f3ff", ElectricBlueLight: "#e8f3ff",
WhiteGreen: '#dafef4',
MintGreen: "#69FABD", MintGreen: "#69FABD",
DarkGreen: '#019267',
MidnightBlue: "#1B295E", MidnightBlue: "#1B295E",
WhiteBrown: '#f7eacc',
GoldenBrown: '#b47d00'
}; };
export const theme = { export const theme = {
@ -16,7 +22,6 @@ export const theme = {
color: "transparent", color: "transparent",
}, },
}, },
colors: { colors: {
text: '#55626d', text: '#55626d',
brand: palette.Purple, brand: palette.Purple,
@ -31,13 +36,19 @@ export const theme = {
minorText: palette.CoolGray, minorText: palette.CoolGray,
iconMain: palette.ElectricBlue, iconMain: palette.ElectricBlue,
tableRow: palette.ElectricBlueLight, tableRow: palette.ElectricBlueLight,
tableRowHover: palette.WhiteGrey,
mintGreen: palette.MintGreen, mintGreen: palette.MintGreen,
errorText: "#ff0000", errorText: "#ff0000",
successText: "#14a266", successText: "#14a266",
backgroundError: "rgba(230, 0, 0, 0.4)", backgroundError: "rgba(230, 0, 0, 0.4)",
backgroundSuccess: "rgb(106 250 188 / 44%)", backgroundSuccess: "rgb(106 250 188 / 44%)",
backgroundToaster: "rgba(0, 174, 233, 0.7)", backgroundToaster: "rgba(0, 174, 233, 0.7)",
backgroundTip: '#f3f3f3' backgroundTip: palette.MidnightBlue,
backgroundMark: palette.WhiteBlue,
warning: palette.GoldenBrown,
warningBackground: palette.WhiteBrown,
success: palette.DarkGreen,
successBackground: palette.WhiteGreen,
}, },
palette, palette,
select: { select: {
@ -45,8 +56,6 @@ export const theme = {
color: "brand", color: "brand",
}, },
}, },
font: { font: {
family: "Nunito", family: "Nunito",
// family: "Fira Sans", // family: "Fira Sans",
@ -66,13 +75,13 @@ export const theme = {
borderColor: "brand", borderColor: "brand",
}, },
dataTable: { dataTable: {
border: {
header: {
color: 'border'
}
},
body: { body: {
extend: (props: any) => ` extend: (props: any) => `
tr:first-child {
th, td {
border: none;
}
}
tr { tr {
th, td { th, td {
@ -82,13 +91,11 @@ export const theme = {
text-align: right; text-align: right;
} }
} }
tr:nth-child(even) { tr:hover {
th, td { background-color: ${props.theme.global.colors.tableRowHover};
background-color: ${props.theme.global.colors.tableRow};
}
} }
`, `
}, },
}, },
}; };
@ -121,6 +128,7 @@ export const darkTheme = {
minorText: "#5f98c7", minorText: "#5f98c7",
iconMain: palette.ElectricBlue, iconMain: palette.ElectricBlue,
tableRow: "#122852", tableRow: "#122852",
tableRowHover: '#1b3e7f',
mintGreen: palette.MintGreen, mintGreen: palette.MintGreen,
errorText: "#ff5858", errorText: "#ff5858",
successText: "#00d67b", successText: "#00d67b",
@ -128,7 +136,12 @@ export const darkTheme = {
backgroundSuccess: "rgb(106 250 188 / 23%)", backgroundSuccess: "rgb(106 250 188 / 23%)",
backgroundToaster: "rgb(93 111 181 / 70%)", backgroundToaster: "rgb(93 111 181 / 70%)",
selected: "#3c53a2", selected: "#3c53a2",
backgroundTip: '#22577E' backgroundTip: '#22577E',
backgroundMark: '#3660ad',
warning: palette.GoldenBrown,
warningBackground: palette.WhiteBrown,
success: palette.DarkGreen,
successBackground: palette.WhiteGreen,
}, },
palette, palette,
font: { font: {

Loading…
Cancel
Save