Merge pull request #198 from ArtemKolodko/refactor_txs_columns
Refactor transaction columnspull/199/head
commit
83d83890bd
@ -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 }) => ( |
||||
<Box direction="row" align="center"> |
||||
<Box background="background" direction="row" pad="small" border={{color: 'border', size: '0px' }}> |
||||
<div>{message}</div> |
||||
</Box> |
||||
export const TipContent = (props: { message: string | JSX.Element, showArrow?: boolean }) => { |
||||
let message = props.message |
||||
if (typeof message === 'string') { |
||||
message = <Text size={'small'}>{message}</Text> |
||||
} |
||||
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> |
||||
) |
||||
} |
||||
|
@ -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> |
||||
), |
||||
}, |
||||
]; |
||||
}; |
Loading…
Reference in new issue