diff --git a/.gitignore b/.gitignore index 3381cfe..ca68a81 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ .pnp.js .firebase/* /.firebase +/firebase diff --git a/src/components/block/BlockDetails.tsx b/src/components/block/BlockDetails.tsx index 0af5273..f3a2d5f 100644 --- a/src/components/block/BlockDetails.tsx +++ b/src/components/block/BlockDetails.tsx @@ -20,7 +20,6 @@ const columns = [ } - plain > @@ -101,12 +100,10 @@ export const BlockDetails: FunctionComponent = ({ data={blockData} step={10} border={{ - header: { - color: "none", - }, + header: false, body: { color: "border", - side: "top", + side: "bottom", size: "1px", }, }} diff --git a/src/components/block/BlockList.tsx b/src/components/block/BlockList.tsx index 7008e31..196f89f 100644 --- a/src/components/block/BlockList.tsx +++ b/src/components/block/BlockList.tsx @@ -71,12 +71,10 @@ export const BlockList: FunctionComponent = ({ block }) => { data={blockData} step={10} border={{ - header: { - color: 'none' - }, + header: false, body: { color: 'border', - side: 'top', + side: 'bottom', size: '1px' } }} @@ -91,4 +89,4 @@ export const BlockList: FunctionComponent = ({ block }) => { -} \ No newline at end of file +} diff --git a/src/components/block/helpers.tsx b/src/components/block/helpers.tsx index 083512d..5318ee1 100644 --- a/src/components/block/helpers.tsx +++ b/src/components/block/helpers.tsx @@ -58,6 +58,7 @@ export const blockPropertyDisplayNames: Record = { }; export const blockPropertyDescriptions: Record = { + shard: 'Shard 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.", hash: "The hash of the block header of the current block.", diff --git a/src/components/metrics/index.tsx b/src/components/metrics/index.tsx index d350ee3..eeaf824 100644 --- a/src/components/metrics/index.tsx +++ b/src/components/metrics/index.tsx @@ -311,7 +311,6 @@ function BlockLatency(params: { latency: number; latencyPerBlock: number[] }) { } /> } - plain > {params.latency.toFixed(2)}s diff --git a/src/components/tables/TransactionsTable.tsx b/src/components/tables/TransactionsTable.tsx index ae1dd15..4587353 100644 --- a/src/components/tables/TransactionsTable.tsx +++ b/src/components/tables/TransactionsTable.tsx @@ -22,7 +22,7 @@ function getColumns(props: any) { size: "xxsmall", resizeable: false, header: ( - + Shard ), @@ -43,7 +43,7 @@ function getColumns(props: any) { size: "xsmall", resizeable: false, header: ( - + Hash ), @@ -65,7 +65,7 @@ function getColumns(props: any) { size: "260px", resizeable: false, header: ( - + Block number ), @@ -89,7 +89,7 @@ function getColumns(props: any) { size: "large", resizeable: false, header: ( - + From ), @@ -100,7 +100,7 @@ function getColumns(props: any) { size: "large", resizeable: false, header: ( - + To ), @@ -111,7 +111,7 @@ function getColumns(props: any) { size: "380px", resizeable: false, header: ( - + ONEValue ), @@ -125,7 +125,7 @@ function getColumns(props: any) { property: "timestamp", resizeable: false, header: ( - + Timestamp ), @@ -195,8 +195,7 @@ export function TransactionsTable(props: TransactionTableProps) { direction="row" justify={hidePagination ? "start" : "between"} pad={{ bottom: "small" }} - margin={{ bottom: "small" }} - border={{ size: "xsmall", side: "bottom", color: "border" }} + margin={{ bottom: "0" }} > {!hideCounter ? ( @@ -239,15 +238,22 @@ export function TransactionsTable(props: TransactionTableProps) { [ content={ + {transactionPropertyDescriptions[e.key + type] || + transactionPropertyDescriptions[e.key]} + } /> } - plain > @@ -187,12 +188,10 @@ export const TransactionDetails: FunctionComponent = ({ data={txData} step={10} border={{ - header: { - color: "none", - }, + header: false, body: { color: "border", - side: "top", + side: "bottom", size: "1px", }, }} diff --git a/src/components/transaction/helpers.tsx b/src/components/transaction/helpers.tsx index b2765c1..a37fd56 100644 --- a/src/components/transaction/helpers.tsx +++ b/src/components/transaction/helpers.tsx @@ -91,6 +91,7 @@ export const transactionPropertySort: Record = { }; export const transactionPropertyDescriptions: Record = { + Status: "The status of the transaction", shardID: "The shard number where the transaction belongs.", 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.", diff --git a/src/components/ui/Address.tsx b/src/components/ui/Address.tsx index 5e42d47..f3ac1ff 100644 --- a/src/components/ui/Address.tsx +++ b/src/components/ui/Address.tsx @@ -27,6 +27,7 @@ interface IAddress { displayHash?: boolean; noHistoryPush?: boolean; hideCopyBtn?: boolean; + showLink?: boolean; } const AddressText = styled(Text)<{ isShortEllipsis?: boolean }>` @@ -48,6 +49,7 @@ export const Address = (props: IAddress) => { color = "brand", displayHash, hideCopyBtn = false, + showLink = true } = props; const history = useHistory(); const ERC20Map = useERC20Pool(); @@ -81,13 +83,39 @@ export const Address = (props: IAddress) => { parsedName = address === EMPTY_ADDRESS ? "0x0" : parsedName; - let outPutAddress = address; + let outPutAddress: string; try { outPutAddress = currency === "ONE" ? getAddress(address).bech32 : toChecksumAddress(address); } catch { outPutAddress = address; } + const addressContent = { + e.preventDefault(); + history.push(`/${type}/${address}`); + } + } + > + {parsedName || + (isShort + ? `${outPutAddress.substr(0, 4)}...${outPutAddress.substr(-4)}` + : outPutAddress)} + + return (
@@ -108,39 +136,14 @@ export const Address = (props: IAddress) => { }} /> )} - - { - e.preventDefault(); - history.push(`/${type}/${address}`); - } - } - > - {parsedName || - (isShort - ? `${outPutAddress.substr(0, 4)}...${outPutAddress.substr(-4)}` - : outPutAddress)} - - + {showLink && + + {addressContent} + + } + {!showLink && + addressContent + }
); diff --git a/src/components/ui/ONEValue.tsx b/src/components/ui/ONEValue.tsx index 2553f65..dc5d4b5 100644 --- a/src/components/ui/ONEValue.tsx +++ b/src/components/ui/ONEValue.tsx @@ -56,25 +56,25 @@ export const ONEValue = (props: ONEValueProps) => {
{USDValue && +price > 0 && !isTodayTransaction && !hideTip && ( + {`Displaying value on ${dayjs(timestamp).format( "YYYY-MM-DD" - )}. Current value`}{" "} + )}. Current value:`}{" "} $ {formatNumber(v * +lastPrice, { maximumFractionDigits: 2, })} -
+ } /> } - plain > (${USDValue})
diff --git a/src/components/ui/Pagination/index.tsx b/src/components/ui/Pagination/index.tsx index ff0522a..75742bd 100644 --- a/src/components/ui/Pagination/index.tsx +++ b/src/components/ui/Pagination/index.tsx @@ -173,6 +173,8 @@ export function PaginationRecordsPerPage(props: ElementsPerPage) { onChange(newFilter); }; + const renderOption = (option: string) => {option} + return ( @@ -180,7 +182,7 @@ export function PaginationRecordsPerPage(props: ElementsPerPage) { options={options} value={limit.toString()} onChange={onChangeLimit} - /> + >{renderOption} records per page diff --git a/src/components/ui/Tooltip.tsx b/src/components/ui/Tooltip.tsx index 65b2ddb..0f2eb0a 100644 --- a/src/components/ui/Tooltip.tsx +++ b/src/components/ui/Tooltip.tsx @@ -1,13 +1,34 @@ import React from 'react' +import { Box, Text } from 'grommet' +import styled from "styled-components"; -import { grommet, Box, Button, Grommet, Heading, Text, Tip } from 'grommet' -import { Trash } from 'grommet-icons' +const ArrowDown = styled(Box)` + 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 = ({ message }) => ( - - -
{message}
-
+export const TipContent = (props: { message: string | JSX.Element, showArrow?: boolean }) => { + let message = props.message + if (typeof message === 'string') { + message = {message} + } + return + {message} + {props.showArrow && + + } -) \ No newline at end of file +} diff --git a/src/index.css b/src/index.css index 56bcf06..b4af14f 100644 --- a/src/index.css +++ b/src/index.css @@ -29,12 +29,25 @@ a { background: transparent !important; } +/* Tooltip override */ body.light > div > div[aria-hidden=false] > div > div { - background: #fff !important; - border: 1px solid #f3f3f3; + box-shadow: none; + background: unset; } body.dark > div > div[aria-hidden=false] > div > div { - background: #1b2a5e !important; - border: 1px solid #3a54b3; -} \ No newline at end of file + box-shadow: none; + 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; +} diff --git a/src/pages/AddressPage/AddressDetails.tsx b/src/pages/AddressPage/AddressDetails.tsx index bc4eab0..12b77fb 100644 --- a/src/pages/AddressPage/AddressDetails.tsx +++ b/src/pages/AddressPage/AddressDetails.tsx @@ -262,15 +262,15 @@ const addressPropertyDisplayValues: Record< formatNumber /> } - plain > @@ -283,15 +283,15 @@ const addressPropertyDisplayValues: Record< <>{formatNumber(+value)} } - plain > @@ -312,15 +312,15 @@ const addressPropertyDisplayValues: Record< formatNumber /> } - plain > diff --git a/src/pages/AddressPage/TokenInfo.tsx b/src/pages/AddressPage/TokenInfo.tsx index 4510fe4..89ff278 100644 --- a/src/pages/AddressPage/TokenInfo.tsx +++ b/src/pages/AddressPage/TokenInfo.tsx @@ -179,7 +179,6 @@ export function TokensInfo(props: { value: Token[] }) { } - plain > diff --git a/src/pages/AddressPage/index.tsx b/src/pages/AddressPage/index.tsx index 2f8b168..949d447 100644 --- a/src/pages/AddressPage/index.tsx +++ b/src/pages/AddressPage/index.tsx @@ -16,7 +16,7 @@ import { useHistory, useParams } from "react-router-dom"; import { useERC20Pool } from "src/hooks/ERC20_Pool"; import { useERC721Pool } from "src/hooks/ERC721_Pool"; import { useERC1155Pool } from "src/hooks/ERC1155_Pool"; -import { Transactions } from "./tabs/Transactions"; +import { Transactions } from "./tabs/transactions/Transactions"; import { IUserERC721Assets, TRelatedTransaction, diff --git a/src/pages/AddressPage/tabs/events/Events.tsx b/src/pages/AddressPage/tabs/events/Events.tsx index ff67aef..1f3fec6 100644 --- a/src/pages/AddressPage/tabs/events/Events.tsx +++ b/src/pages/AddressPage/tabs/events/Events.tsx @@ -10,6 +10,7 @@ import { } from "../../../../api/client"; import styled from "styled-components"; import { DisplaySignature, parseSuggestedEvent } from "../../../../web3/parseByteCode"; +import { TipContent } from "../../../../components/ui"; const TopicsContainer = styled.div` text-align: left; @@ -66,12 +67,15 @@ const CenteredContainer = styled.div` function TxsHashColumn (props: { log: LogWithSignature }) { const { log } = props return
- + } + > {log.transactionHash} - + }>
@@ -80,7 +84,10 @@ function TxsHashColumn (props: { log: LogWithSignature }) {
{log.timestamp && - + } + >
{dayjs(log.timestamp).fromNow()}
} @@ -92,7 +99,7 @@ function TxMethod (props: { log: LogWithSignature }) { return
- + }> {props.log.input.slice(0, 10)} diff --git a/src/pages/AddressPage/tabs/Transactions.tsx b/src/pages/AddressPage/tabs/transactions/Transactions.tsx similarity index 96% rename from src/pages/AddressPage/tabs/Transactions.tsx rename to src/pages/AddressPage/tabs/transactions/Transactions.tsx index a8affbe..cc93c9f 100644 --- a/src/pages/AddressPage/tabs/Transactions.tsx +++ b/src/pages/AddressPage/tabs/transactions/Transactions.tsx @@ -14,14 +14,14 @@ import { } from "src/types"; import { TRelatedTransaction } from "src/api/client.interface"; import { getAddress, mapBlockchainTxToRelated } from "src/utils"; -import { ExportToCsvButton } from "../../../components/ui/ExportToCsvButton"; +import { ExportToCsvButton } from "../../../../components/ui/ExportToCsvButton"; import { hmyv2_getStakingTransactionsCount, hmyv2_getStakingTransactionsHistory, hmyv2_getTransactionsCount, hmyv2_getTransactionsHistory -} from "../../../api/rpc"; -import { getColumns, getERC20Columns, getNFTColumns, getStackingColumns } from "./txsColumns"; -import useQuery from "../../../hooks/useQuery"; +} from "../../../../api/rpc"; +import { getColumns, getERC20Columns, getNFTColumns, getStakingColumns } from "./columns"; +import useQuery from "../../../../hooks/useQuery"; const internalTxsBlocksFrom = 23000000 const allowedLimits = [10, 25, 50, 100] @@ -217,7 +217,7 @@ export function Transactions(props: { switch (props.type) { case "staking_transaction": { - columns = getStackingColumns(id); + columns = getStakingColumns(id); break; } case "erc20": { diff --git a/src/pages/AddressPage/tabs/transactions/columns/common.tsx b/src/pages/AddressPage/tabs/transactions/columns/common.tsx new file mode 100644 index 0000000..134f165 --- /dev/null +++ b/src/pages/AddressPage/tabs/transactions/columns/common.tsx @@ -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 +
+ +} + +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 + {direction.toUpperCase()} + +} diff --git a/src/pages/AddressPage/tabs/transactions/columns/erc20.tsx b/src/pages/AddressPage/tabs/transactions/columns/erc20.tsx new file mode 100644 index 0000000..191b201 --- /dev/null +++ b/src/pages/AddressPage/tabs/transactions/columns/erc20.tsx @@ -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[] { + return [ + { + property: 'hash', + header: ( + + Hash + + ), + render: (data: any) => ( +
+ ) + }, + { + property: 'event', + header: ( + + Event + + ), + render: (data: any) => { + const eventType = data.eventType || '-' + return ( + {eventType} + ) + } + }, + { + property: 'from', + header: ( + + From + + ), + render: (data: RelatedTransaction) => + }, + { + property: 'marker', + header: <>, + render: (data: RelatedTransaction) => + }, + { + property: 'to', + header: ( + + To + + ), + render: (data: RelatedTransaction) => + }, + { + property: 'value', + header: ( + + Value + + ), + render: (data: any) => { + const { address, value, eventType } = data + + if (!value) { + return '?' + } + + if (eventType === 'Approval') { + return ( + + + ) + } + + return ( + + + ) + } + }, + { + property: 'token', + header: ( + + Token + + ), + render: (data: any) => { + const address = data.address ? data.address : '—' + + return ( + +
+ + ) + } + }, + { + property: 'timestamp', + header: ( + + Timestamp + + ), + render: (data: RelatedTransaction) => ( + + + + ) + } + ] +} diff --git a/src/pages/AddressPage/tabs/transactions/columns/index.tsx b/src/pages/AddressPage/tabs/transactions/columns/index.tsx new file mode 100644 index 0000000..ed88294 --- /dev/null +++ b/src/pages/AddressPage/tabs/transactions/columns/index.tsx @@ -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, +} diff --git a/src/pages/AddressPage/tabs/transactions/columns/nft.tsx b/src/pages/AddressPage/tabs/transactions/columns/nft.tsx new file mode 100644 index 0000000..b1cd42e --- /dev/null +++ b/src/pages/AddressPage/tabs/transactions/columns/nft.tsx @@ -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[] { + return [ + { + property: "hash", + header: ( + + Hash + + ), + render: (data: any) => ( +
+ ), + }, + { + property: 'event', + header: ( + + Event + + ), + render: (data: any) => { + const eventType = data.eventType || '-' + return ( + {eventType} + ) + } + }, + { + property: "from", + header: ( + + From + + ), + render: (data: RelatedTransaction) => , + }, + { + property: "marker", + header: <>, + render: (data: RelatedTransaction) => , + }, + { + property: "to", + header: ( + + To + + ), + render: (data: RelatedTransaction) => data.to.trim() && , + }, + // { + // property: "value", + // header: ( + // + // Value + // + // ), + // render: (data: RelatedTransaction) => ( + // + // + // + // ), + // }, + { + property: 'tokenId', + header: ( + + TokenId + + ), + render: (data: any) => { + const tokenId = extractTokenId(data) + return ( + + + {tokenId} + + + ) + } + }, + { + property: 'token', + header: ( + + Token + + ), + render: (data: any) => { + const address = data.address ? data.address : '—' + + return ( + +
+ + ) + } + }, + { + property: "timestamp", + header: ( + + Timestamp + + ), + render: (data: RelatedTransaction) => ( + + + + ), + }, + ]; +} diff --git a/src/pages/AddressPage/tabs/transactions/columns/staking.tsx b/src/pages/AddressPage/tabs/transactions/columns/staking.tsx new file mode 100644 index 0000000..1d43721 --- /dev/null +++ b/src/pages/AddressPage/tabs/transactions/columns/staking.tsx @@ -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[] => { + return [ + { + property: "hash", + header: ( + + Hash + + ), + render: (data: any) => ( +
+ ), + }, + { + property: "type", + header: ( + + Type + + ), + render: (data: RelatedTransaction) => ( + + {data.type} + + ), + }, + { + property: "validator", + header: ( + + Validator + + ), + render: (data: RelatedTransaction) => data.msg?.validatorAddress + ? + : '—' + }, + { + property: "marker", + header: <>, + render: (data: RelatedTransaction) => , + }, + { + property: "delegator", + header: ( + + Delegator + + ), + render: (data: RelatedTransaction) => data.msg?.delegatorAddress + ? + : '—', + }, + { + property: "value", + header: ( + + Value + + ), + render: (data: RelatedTransaction) => ( + + {data.msg?.amount ? ( + + ) : data.amount ? ( + + ) : ( + "—" + )} + + ), + }, + { + property: "timestamp", + header: ( + + Timestamp + + ), + render: (data: RelatedTransaction) => ( + + + + ), + }, + ]; +}; diff --git a/src/pages/AddressPage/tabs/transactions/columns/transactions.tsx b/src/pages/AddressPage/tabs/transactions/columns/transactions.tsx new file mode 100644 index 0000000..844e0d1 --- /dev/null +++ b/src/pages/AddressPage/tabs/transactions/columns/transactions.tsx @@ -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[] { + return [ + // { + // property: "type", + // size: "", + // header: ( + // + // Type + // + // ), + // render: (data: RelatedTransaction) => ( + // + // {relatedTxMap[data.transactionType] || data.transactionType} + // + // ), + // }, + { + property: "hash", + header: ( + + Hash + + ), + render: (data: any) => ( +
+ ), + }, + { + property: "method", + header: ( + + Method + + ), + 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 {"—"}; + } + + const tipContent = {signature}} + /> + + return ( + + {signature} + + ); + }, + }, + // { + // property: "shard", + // header: ( + // + // Shard + // + // ), + // render: (data: RelatedTransaction) => ( + // + // {0} + // + // {0} + // + // ), + // }, + { + property: "from", + header: ( + + From + + ), + render: (data: RelatedTransaction) => , + }, + { + property: "marker", + header: <>, + render: (data: RelatedTransaction) => , + }, + { + property: "to", + header: ( + + To + + ), + render: (data: RelatedTransaction) => , + }, + { + property: "value", + header: ( + + Value + + ), + render: (data: RelatedTransaction) => ( + + + + ), + }, + + { + property: "timestamp", + header: ( + + Timestamp + + ), + render: (data: RelatedTransaction) => ( + + + + ), + }, + ]; +} diff --git a/src/pages/AddressPage/tabs/txsColumns.tsx b/src/pages/AddressPage/tabs/txsColumns.tsx deleted file mode 100644 index cc635cc..0000000 --- a/src/pages/AddressPage/tabs/txsColumns.tsx +++ /dev/null @@ -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 - {direction.toUpperCase()} - -} - -export function getERC20Columns(id: string): ColumnConfig[] { - return [ - { - property: 'hash', - header: ( - - Hash - - ), - render: (data: any) => ( -
- ) - }, - { - property: 'event', - header: ( - - Event - - ), - render: (data: any) => { - const eventType = data.eventType || '-' - - return ( - - - {eventType} - - - ) - } - }, - { - property: 'from', - header: ( - - From - - ), - render: (data: RelatedTransaction) => { - const { from } = data - return ( - -
- ) - } - }, - { - property: 'marker', - header: <>, - render: (data: RelatedTransaction) => - }, - { - property: 'to', - header: ( - - To - - ), - render: (data: RelatedTransaction) => { - const { to } = data - return ( - -
- ) - } - }, - { - property: 'value', - header: ( - - Value - - ), - render: (data: any) => { - const { address, value, eventType } = data - - if (!value) { - return '?' - } - - if (eventType === 'Approval') { - return ( - - - ) - } - - return ( - - - ) - } - }, - { - property: 'token', - header: ( - - Token - - ), - render: (data: any) => { - const address = data.address ? data.address : '—' - - return ( - -
- - ) - } - }, - { - property: 'timestamp', - header: ( - - Timestamp - - ), - render: (data: RelatedTransaction) => ( - - - - ) - } - ] -} - -export function getColumns(id: string): ColumnConfig[] { - return [ - // { - // property: "type", - // size: "", - // header: ( - // - // Type - // - // ), - // render: (data: RelatedTransaction) => ( - // - // {relatedTxMap[data.transactionType] || data.transactionType} - // - // ), - // }, - { - property: "hash", - header: ( - - Hash - - ), - render: (data: any) => ( -
- ), - }, - { - property: "method", - header: ( - - Method - - ), - 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 {"—"}; - } - - return ( - {signature}}> - - - {signature} - - - - ); - }, - }, - // { - // property: "shard", - // header: ( - // - // Shard - // - // ), - // render: (data: RelatedTransaction) => ( - // - // {0} - // - // {0} - // - // ), - // }, - { - property: "from", - header: ( - - From - - ), - render: (data: RelatedTransaction) => ( - -
- - ), - }, - { - property: "marker", - header: <>, - render: (data: RelatedTransaction) => , - }, - { - property: "to", - header: ( - - To - - ), - render: (data: RelatedTransaction) => ( - -
- - ), - }, - { - property: "value", - header: ( - - Value - - ), - render: (data: RelatedTransaction) => ( - - - - ), - }, - - { - property: "timestamp", - header: ( - - Timestamp - - ), - render: (data: RelatedTransaction) => ( - - - - ), - }, - ]; -} - -export function getNFTColumns(id: string): ColumnConfig[] { - return [ - { - property: "hash", - header: ( - - Hash - - ), - render: (data: any) => ( -
- ), - }, - { - property: 'event', - header: ( - - Event - - ), - render: (data: any) => { - const eventType = data.eventType || '-' - - return ( - - - {eventType} - - - ) - } - }, - { - property: "from", - header: ( - - From - - ), - render: (data: RelatedTransaction) => ( - -
- - ), - }, - { - property: "marker", - header: <>, - render: (data: RelatedTransaction) => , - }, - { - property: "to", - header: ( - - To - - ), - render: (data: RelatedTransaction) => { - return ( - - {data.to.trim() &&
} - - ) - }, - }, - // { - // property: "value", - // header: ( - // - // Value - // - // ), - // render: (data: RelatedTransaction) => ( - // - // - // - // ), - // }, - { - property: 'tokenId', - header: ( - - TokenId - - ), - render: (data: any) => { - const tokenId = extractTokenId(data) - return ( - - - {tokenId} - - - ) - } - }, - { - property: 'token', - header: ( - - Token - - ), - render: (data: any) => { - const address = data.address ? data.address : '—' - - return ( - -
- - ) - } - }, - { - property: "timestamp", - header: ( - - Timestamp - - ), - render: (data: RelatedTransaction) => ( - - - - ), - }, - ]; -} - -export const getStackingColumns = (id: string): ColumnConfig[] => { - return [ - { - property: "hash", - header: ( - - Hash - - ), - render: (data: any) => ( -
- ), - }, - { - property: "type", - header: ( - - Type - - ), - render: (data: RelatedTransaction) => ( - - {data.type} - - ), - }, - { - property: "validator", - header: ( - - Validator - - ), - render: (data: RelatedTransaction) => ( - - {data.msg?.validatorAddress ? ( -
- ) : ( - "—" - )} - - ), - }, - { - property: "marker", - header: <>, - render: (data: RelatedTransaction) => , - }, - { - property: "delegator", - header: ( - - Delegator - - ), - render: (data: RelatedTransaction) => ( - - {data.msg?.delegatorAddress ? ( -
- ) : ( - "—" - )} - - ), - }, - { - property: "value", - header: ( - - Value - - ), - render: (data: RelatedTransaction) => ( - - {data.msg?.amount ? ( - - ) : data.amount ? ( - - ) : ( - "—" - )} - - ), - }, - { - property: "timestamp", - header: ( - - Timestamp - - ), - render: (data: RelatedTransaction) => ( - - - - ), - }, - ]; -}; diff --git a/src/pages/AllBlocksPage/AllBlocksTable.tsx b/src/pages/AllBlocksPage/AllBlocksTable.tsx index 1346eef..34be7c2 100644 --- a/src/pages/AllBlocksPage/AllBlocksTable.tsx +++ b/src/pages/AllBlocksPage/AllBlocksTable.tsx @@ -167,14 +167,21 @@ export function AllBlocksTable() { {data.name} {data.isBridged &&
- + }>
} @@ -204,12 +203,11 @@ function getColumns(props: any) { dropProps={{ align: { right: "left" } }} content={ } - plain > diff --git a/src/pages/ERC20List/ERC20Table.tsx b/src/pages/ERC20List/ERC20Table.tsx index 59ba0de..403b5f7 100644 --- a/src/pages/ERC20List/ERC20Table.tsx +++ b/src/pages/ERC20List/ERC20Table.tsx @@ -87,15 +87,14 @@ export function ERC20Table(props: TransactionTableProps) { primaryKey={'address'} data={data} border={{ - header: { - color: "brand", - }, + header: false, body: { color: "border", - side: "top", + side: "bottom", size: "1px", }, }} + background={{header: 'unset'}} />
{data.name} {data.isBridged &&
- + }>
} @@ -181,12 +180,11 @@ function getColumns(props: any) { dropProps={{ align: { left: "right" } }} content={ } - plain > @@ -218,12 +216,11 @@ function getColumns(props: any) { dropProps={{ align: { right: "left" } }} content={ } - plain > @@ -251,12 +248,11 @@ function getColumns(props: any) { dropProps={{ align: { right: "left" } }} content={ } - plain > diff --git a/src/pages/ERC721List/ERC721Table.tsx b/src/pages/ERC721List/ERC721Table.tsx index 172bee1..e22bc20 100644 --- a/src/pages/ERC721List/ERC721Table.tsx +++ b/src/pages/ERC721List/ERC721Table.tsx @@ -86,15 +86,14 @@ export function ERC721Table(props: TransactionTableProps) { data={data} primaryKey={'address'} border={{ - header: { - color: "brand", - }, + header: false, body: { color: "border", - side: "top", + side: "bottom", size: "1px", }, }} + background={{header: 'unset'}} />
{data.name} {data.isBridged &&
- + }>
} @@ -191,12 +190,11 @@ function getColumns(props: any) { dropProps={{ align: { right: "left" } }} content={ } - plain > diff --git a/src/pages/ExportData/index.tsx b/src/pages/ExportData/index.tsx index f2fa221..1e4a08f 100644 --- a/src/pages/ExportData/index.tsx +++ b/src/pages/ExportData/index.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; 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 styled from "styled-components"; import useQuery from "../../hooks/useQuery"; @@ -118,7 +118,7 @@ export const ExportData = () => {
- Select start date}> + }> {
to
- Select end date}> + }> { data={params.blocks} step={10} border={{ - header: { - color: "brand", - }, + header: false, body: { color: "border", - side: "top", + side: "bottom", size: "1px", }, }} + background={{header: 'unset'}} />
); diff --git a/src/pages/MainPage/LatestTransactionsTable.tsx b/src/pages/MainPage/LatestTransactionsTable.tsx index 947aea5..d082934 100644 --- a/src/pages/MainPage/LatestTransactionsTable.tsx +++ b/src/pages/MainPage/LatestTransactionsTable.tsx @@ -154,21 +154,20 @@ export function LatestTransactionsTable() { return ( ); diff --git a/src/pages/tools/CheckHRC/index.tsx b/src/pages/tools/CheckHRC/index.tsx index d4492ed..d84c6c2 100644 --- a/src/pages/tools/CheckHRC/index.tsx +++ b/src/pages/tools/CheckHRC/index.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import { Box, Heading, Select, Spinner, Text, TextArea, TextInput, Tip } from "grommet"; 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 { getContractsByField } from "../../../api/client"; import styled from "styled-components"; @@ -230,8 +230,18 @@ export function CheckHRC() { {(!isContractLoading && contractAddress) && {isAllEventsMatched - ? - : } + ? } + > + + + : } + > + + } }
diff --git a/src/theme.ts b/src/theme.ts index 10007ff..4126961 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -1,12 +1,18 @@ export const palette = { + WhiteGrey: "#F4F7F9", LightGrey: "#e7ecf7", Grey: "#b1b1b1", CoolGray: "#758796", + WhiteBlue: '#EFF8FF', Purple: "#00AEE9", ElectricBlue: "#00AEE9", ElectricBlueLight: "#e8f3ff", + WhiteGreen: '#dafef4', MintGreen: "#69FABD", + DarkGreen: '#019267', MidnightBlue: "#1B295E", + WhiteBrown: '#f7eacc', + GoldenBrown: '#b47d00' }; export const theme = { @@ -16,7 +22,6 @@ export const theme = { color: "transparent", }, }, - colors: { text: '#55626d', brand: palette.Purple, @@ -31,13 +36,19 @@ export const theme = { minorText: palette.CoolGray, iconMain: palette.ElectricBlue, tableRow: palette.ElectricBlueLight, + tableRowHover: palette.WhiteGrey, mintGreen: palette.MintGreen, errorText: "#ff0000", successText: "#14a266", backgroundError: "rgba(230, 0, 0, 0.4)", backgroundSuccess: "rgb(106 250 188 / 44%)", 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, select: { @@ -45,8 +56,6 @@ export const theme = { color: "brand", }, }, - - font: { family: "Nunito", // family: "Fira Sans", @@ -66,13 +75,13 @@ export const theme = { borderColor: "brand", }, dataTable: { + border: { + header: { + color: 'border' + } + }, body: { extend: (props: any) => ` - tr:first-child { - th, td { - border: none; - } - } tr { th, td { @@ -82,13 +91,11 @@ export const theme = { text-align: right; } } - - tr:nth-child(even) { - th, td { - background-color: ${props.theme.global.colors.tableRow}; - } + + tr:hover { + background-color: ${props.theme.global.colors.tableRowHover}; } - `, + ` }, }, }; @@ -121,6 +128,7 @@ export const darkTheme = { minorText: "#5f98c7", iconMain: palette.ElectricBlue, tableRow: "#122852", + tableRowHover: '#1b3e7f', mintGreen: palette.MintGreen, errorText: "#ff5858", successText: "#00d67b", @@ -128,7 +136,12 @@ export const darkTheme = { backgroundSuccess: "rgb(106 250 188 / 23%)", backgroundToaster: "rgb(93 111 181 / 70%)", selected: "#3c53a2", - backgroundTip: '#22577E' + backgroundTip: '#22577E', + backgroundMark: '#3660ad', + warning: palette.GoldenBrown, + warningBackground: palette.WhiteBrown, + success: palette.DarkGreen, + successBackground: palette.WhiteGreen, }, palette, font: {