667 lines
34 KiB
JavaScript
667 lines
34 KiB
JavaScript
import { useEffect, useState, useMemo, useCallback } from "react";
|
|
|
|
import {
|
|
Box,
|
|
Container,
|
|
Typography,
|
|
Link,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableRow,
|
|
TableContainer,
|
|
useMediaQuery,
|
|
useTheme
|
|
} from "@mui/material";
|
|
import { ss58Decode } from "@polkadot-labs/hdkd-helpers";
|
|
import { toHex } from "@polkadot-api/utils";
|
|
import { useBlockNumber, useTransactionConfirmations } from "wagmi";
|
|
|
|
import PendingActionsIcon from '@mui/icons-material/PendingActions';
|
|
import PublicIcon from '@mui/icons-material/Public';
|
|
import ArrowRightIcon from '@mui/icons-material/ArrowRight';
|
|
import HourglassBottomIcon from '@mui/icons-material/HourglassBottom';
|
|
import ThumbUpIcon from '@mui/icons-material/ThumbUp';
|
|
import ThumbDownAltIcon from '@mui/icons-material/ThumbDownAlt';
|
|
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
|
import CheckIcon from '@mui/icons-material/Check';
|
|
|
|
import PageTitle from "../../components/PageTitle/PageTitle";
|
|
import Paper from "../../components/Paper/Paper";
|
|
import SwapCard from "../../components/Swap/SwapCard";
|
|
import SwapCollection from "../../components/Swap/SwapCollection";
|
|
import TokenStack from "../../components/TokenStack/TokenStack";
|
|
import GhostStyledIcon from "../../components/Icon/GhostIcon";
|
|
import Modal from "../../components/Modal/Modal";
|
|
import { PrimaryButton } from "../../components/Button";
|
|
|
|
import { GATEKEEPER_ADDRESSES } from "../../constants/addresses";
|
|
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
|
import { formatCurrency } from "../../helpers";
|
|
|
|
import { useTokenSymbol, useBalance } from "../../hooks/tokens";
|
|
import { useGatekeeperAddress, ghost } from "../../hooks/staking";
|
|
|
|
const STORAGE_PREFIX = "storedTransactions"
|
|
|
|
const Bridge = ({ chainId, address, config, connect }) => {
|
|
const theme = useTheme();
|
|
const isSmallScreen = useMediaQuery("(max-width: 650px)");
|
|
const isSemiSmallScreen = useMediaQuery("(max-width: 480px)");
|
|
const isVerySmallScreen = useMediaQuery("(max-width: 379px)");
|
|
|
|
const [isPending, setIsPending] = useState(false);
|
|
const [bridgeAction, setBridgeAction] = useState(true);
|
|
const [activeTxIndex, setActiveTxIndex] = useState(-1);
|
|
const [txStep, setTxStep] = useState(0);
|
|
const [receiver, setReceiver] = useState("");
|
|
const [convertedReceiver, setConvertedReceiver] = useState(undefined);
|
|
const [amount, setAmount] = useState("");
|
|
|
|
// ReceivedClaps && ApplausesForTransaction
|
|
// session_index
|
|
// transaction_hash
|
|
// keccak256(receiver, amount, chain_id)
|
|
|
|
// const initialStoredTransactions = sessionStorage.getItem(STORAGE_PREFIX);
|
|
const initialStoredTransactions = JSON.stringify([
|
|
{
|
|
sessionIndex: 23,
|
|
transactionHash: "0x11111111111111111",
|
|
receiver: "sfAasdadasasads",
|
|
amount: "2312323232223232",
|
|
chainId: 11155111,
|
|
timestamp: Date.now()
|
|
},
|
|
{
|
|
sessionIndex: 23,
|
|
transactionHash: "0x2222222222222222222",
|
|
receiver: "sfAasdadasasads",
|
|
amount: "1122232232",
|
|
chainId: 1,
|
|
timestamp: Date.now()
|
|
},
|
|
{
|
|
sessionIndex: 24,
|
|
transactionHash: "0x333333333333333333",
|
|
receiver: "sfAasdadasasads",
|
|
amount: "99999999999999992",
|
|
chainId: 11155111,
|
|
timestamp: Date.now()
|
|
},
|
|
{
|
|
sessionIndex: 23,
|
|
transactionHash: "0x4444444444444444444",
|
|
receiver: "sfAasdadasasads",
|
|
amount: "2312323232223232",
|
|
chainId: 11155111,
|
|
timestamp: Date.now()
|
|
},
|
|
{
|
|
sessionIndex: 23,
|
|
transactionHash: "0x555555555555555555555",
|
|
receiver: "sfAasdadasasads",
|
|
amount: "1122232232",
|
|
chainId: 1,
|
|
timestamp: Date.now()
|
|
},
|
|
{
|
|
sessionIndex: 24,
|
|
transactionHash: "0x66666666666666666666666666",
|
|
receiver: "sfAasdadasasads",
|
|
amount: "99999999999999992",
|
|
chainId: 11155111,
|
|
timestamp: Date.now()
|
|
},
|
|
{
|
|
sessionIndex: 23,
|
|
transactionHash: "0x77777777777777777777777777",
|
|
receiver: "sfAasdadasasads",
|
|
amount: "2312323232223232",
|
|
chainId: 11155111,
|
|
timestamp: Date.now()
|
|
},
|
|
{
|
|
sessionIndex: 23,
|
|
transactionHash: "0x888888888888888888888888888",
|
|
receiver: "sfAasdadasasads",
|
|
amount: "1122232232",
|
|
chainId: 1,
|
|
timestamp: Date.now()
|
|
},
|
|
{
|
|
sessionIndex: 24,
|
|
transactionHash: "0x999999999999999999999",
|
|
receiver: "sfAasdadasasads",
|
|
amount: "99999999999999992",
|
|
chainId: 11155111,
|
|
timestamp: Date.now()
|
|
},
|
|
{
|
|
sessionIndex: 23,
|
|
transactionHash: "0x10101010101010101010",
|
|
receiver: "sfAasdadasasads",
|
|
amount: "2312323232223232",
|
|
chainId: 11155111,
|
|
timestamp: Date.now()
|
|
},
|
|
{
|
|
sessionIndex: 23,
|
|
transactionHash: "0x12121212121212212",
|
|
receiver: "sfAasdadasasads",
|
|
amount: "1122232232",
|
|
chainId: 1,
|
|
timestamp: Date.now()
|
|
},
|
|
{
|
|
sessionIndex: 24,
|
|
transactionHash: "0x1313131313131313131",
|
|
receiver: "sfAasdadasasads",
|
|
amount: "99999999999999992",
|
|
chainId: 11155111,
|
|
timestamp: Date.now()
|
|
}
|
|
]);
|
|
const [storedTransactions, setStoredTransactions] = useState(
|
|
initialStoredTransactions ? JSON.parse(initialStoredTransactions) : []
|
|
);
|
|
|
|
const incomingCommission = new DecimalBigNumber(69n, 100);
|
|
const validators = ["first", "second", "third"];
|
|
const clappedValidators = 1;
|
|
|
|
const { data: blockNumber } = useBlockNumber({ watch: true });
|
|
// const { data: txtx } = useTransactionConfirmations({
|
|
// hash: "0xdb30adfa3bfc58539bc3a9a92f0dcace8f251af90f8a4f525b57d95d28103afc",
|
|
// refetchInterval: 5000
|
|
// });
|
|
// console.log(txtx)
|
|
const { gatekeeperAddress } = useGatekeeperAddress(chainId);
|
|
const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST");
|
|
const {
|
|
balance: ghstBalance,
|
|
refetch: ghstBalanceRefetch
|
|
} = useBalance(chainId, "GHST", address);
|
|
|
|
useEffect(() => {
|
|
try {
|
|
const [publicKey, prefix] = ss58Decode(receiver);
|
|
if (prefix !== 1995 && prefix !== 1996) {
|
|
throw new Error("bad prefix");
|
|
}
|
|
setConvertedReceiver(toHex(publicKey));
|
|
} catch {
|
|
setConvertedReceiver(undefined);
|
|
}
|
|
}, [receiver])
|
|
|
|
const chainExplorerUrl = useMemo(() => {
|
|
const client = config?.getClient();
|
|
return client?.chain?.blockExplorers?.default?.url;
|
|
}, [config]);
|
|
|
|
const chainName = useMemo(() => {
|
|
const client = config?.getClient();
|
|
return client?.chain?.name;
|
|
}, [config]);
|
|
|
|
const currentRecord = useMemo(() => {
|
|
if (activeTxIndex === -1) return undefined
|
|
return storedTransactions.at(activeTxIndex)
|
|
}, [activeTxIndex, storedTransactions]);
|
|
|
|
const gatekeeperAddressEmpty = useMemo(() => {
|
|
if (gatekeeperAddress === "0x0000000000000000000000000000000000000000") {
|
|
return true;
|
|
}
|
|
return false;
|
|
}, [gatekeeperAddress]);
|
|
|
|
const preparedAmount = useMemo(() => {
|
|
try {
|
|
const result = BigInt(parseFloat(amount) * Math.pow(10, 18));
|
|
if (result > ghstBalance._value) {
|
|
return ghstBalance._value;
|
|
}
|
|
return result;
|
|
} catch {
|
|
return 0n;
|
|
}
|
|
}, [amount])
|
|
|
|
const filteredStoredTransactions = useMemo(() => {
|
|
return storedTransactions.filter(obj => obj.chainId === chainId);
|
|
}, [storedTransactions, chainId]);
|
|
|
|
const removeStoredRecord = useCallback(() => {
|
|
const newStoredTransactions = storedTransactions.filter((_, index) => index !== activeTxIndex)
|
|
setStoredTransactions(newStoredTransactions);
|
|
sessionStorage.setItem(STORAGE_PREFIX, JSON.stringify(newStoredTransactions));
|
|
setActiveTxIndex(-1);
|
|
}, [storedTransactions, activeTxIndex, setStoredTransactions, setActiveTxIndex]);
|
|
|
|
const handleMouseEnter = (index) => {
|
|
setTxStep(index);
|
|
}
|
|
|
|
const ghostOrConnect = async () => {
|
|
if (address === "") {
|
|
connect();
|
|
} else {
|
|
setIsPending(true);
|
|
|
|
const txHash = await ghost(chainId, address, convertedReceiver, preparedAmount);
|
|
await ghstBalanceRefetch();
|
|
|
|
setReceiver("");
|
|
setAmount("");
|
|
setIsPending(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Box height="calc(100vh - 43px)">
|
|
<PageTitle name="GHOST Bridge" subtitle="The only pure Web3 decentralized bridge." />
|
|
<Container
|
|
style={{
|
|
paddingLeft: isSmallScreen || isVerySmallScreen ? "0" : "3.3rem",
|
|
paddingRight: isSmallScreen || isVerySmallScreen ? "0" : "3.3rem",
|
|
display: "flex",
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
height: "calc(100vh - 153px)"
|
|
}}
|
|
>
|
|
<Modal
|
|
data-testid="transaction-details-modal"
|
|
maxWidth="476px"
|
|
headerContent={
|
|
<Box display="flex" flexDirection="row">
|
|
<Typography variant="h5">
|
|
TX Hash
|
|
<Link
|
|
sx={{
|
|
margin: "0px",
|
|
font: "inherit",
|
|
letterSpacing: "inherit",
|
|
textDecoration: "underline",
|
|
color: theme.colors.gray[10],
|
|
textUnderlineOffset: "0.23rem",
|
|
cursor: "pointer",
|
|
textDecorationThickness: "3px",
|
|
"&:hover": {
|
|
textDecoration: "underline",
|
|
}
|
|
}}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
href={currentRecord
|
|
? `${chainExplorerUrl}/tx/${currentRecord.transactionHash}`
|
|
: ""
|
|
}
|
|
>
|
|
{currentRecord?.transactionHash.slice(0, 9)}...{currentRecord?.transactionHash.slice(-9)}
|
|
</Link>
|
|
</Typography>
|
|
</Box>
|
|
}
|
|
open={activeTxIndex > -1}
|
|
onClose={() => setActiveTxIndex(-1)}
|
|
minHeight={"100px"}
|
|
>
|
|
<Box display="flex" gap="1.5rem" flexDirection="column" marginTop=".8rem">
|
|
<Box display="flex" flexDirection="row" justifyContent="space-between" alignItems="center">
|
|
<Box
|
|
sx={{
|
|
transition: "all 0.2s ease",
|
|
transform: txStep === 0 && "scale(1.1)",
|
|
color: txStep === 0 && theme.colors.primary[300]
|
|
}}
|
|
width="120px"
|
|
display="flex"
|
|
flexDirection="column"
|
|
justifyContent="start"
|
|
alignItems="center"
|
|
onMouseEnter={() => handleMouseEnter(0)}
|
|
>
|
|
<GhostStyledIcon
|
|
sx={{ width: "35px", height: "35px" }}
|
|
viewBox="0 0 25 25"
|
|
component={HourglassBottomIcon}
|
|
/>
|
|
<Typography variant="caption">Finalization</Typography>
|
|
<Typography variant="caption">{blockNumber?.toString()} blocks left</Typography>
|
|
</Box>
|
|
|
|
<GhostStyledIcon
|
|
sx={{ transition: "all 0.2s ease", opacity: txStep < 1 && "0.2" }}
|
|
component={ArrowRightIcon}
|
|
/>
|
|
|
|
<Box
|
|
sx={{
|
|
transition: "all 0.2s ease",
|
|
opacity: txStep < 1 && "0.2",
|
|
transform: txStep === 1 && "scale(1.1)",
|
|
color: txStep === 1 && theme.colors.primary[300]
|
|
}}
|
|
width="120px"
|
|
display="flex"
|
|
flexDirection="column"
|
|
justifyContent="start"
|
|
alignItems="center"
|
|
onMouseEnter={() => handleMouseEnter(1)}
|
|
>
|
|
<Box display="flex" flexDirection="row" justifyContent="center" alignItems="center">
|
|
<GhostStyledIcon
|
|
sx={{ width: "35px", height: "35px" }}
|
|
viewBox="0 0 25 25"
|
|
component={ThumbUpIcon}
|
|
/>
|
|
<GhostStyledIcon
|
|
sx={{ width: "35px", height: "35px" }}
|
|
viewBox="0 0 25 25"
|
|
component={ThumbDownAltIcon}
|
|
/>
|
|
</Box>
|
|
<Box display="flex" flexDirection="column" justifyContent="center" alignItems="center">
|
|
<Typography variant="caption">Slow claps</Typography>
|
|
<Typography variant="caption">{clappedValidators} / {validators.length}</Typography>
|
|
</Box>
|
|
</Box>
|
|
|
|
<GhostStyledIcon
|
|
sx={{ transition: "all 0.2s ease", opacity: txStep < 2 && "0.2" }}
|
|
component={ArrowRightIcon}
|
|
/>
|
|
|
|
<Box
|
|
sx={{
|
|
transition: "all 0.2s ease",
|
|
opacity: txStep < 2 && "0.2",
|
|
transform: txStep === 2 && "scale(1.1)",
|
|
color: txStep === 2 && theme.colors.primary[300]
|
|
}}
|
|
width="120px"
|
|
display="flex"
|
|
flexDirection="column"
|
|
justifyContent="start"
|
|
alignItems="center"
|
|
onMouseEnter={() => handleMouseEnter(2)}
|
|
>
|
|
<GhostStyledIcon
|
|
sx={{ width: "35px", height: "35px" }}
|
|
viewBox="0 0 25 25"
|
|
component={CheckCircleIcon}
|
|
/>
|
|
<Typography variant="caption">Applaused</Typography>
|
|
<Typography variant="caption">Check Receiver</Typography>
|
|
</Box>
|
|
</Box>
|
|
|
|
<Box display="flex" flexDirection="column" gap="5px" padding="0.6rem 0">
|
|
<Box display="flex" flexDirection="row" justifyContent="space-between">
|
|
<Typography variant="body2">Session Index:</Typography>
|
|
<Typography variant="body2">{currentRecord?.sessionIndex}</Typography>
|
|
</Box>
|
|
<Box display="flex" flexDirection="row" justifyContent="space-between">
|
|
<Typography variant="body2">Receiver Address:</Typography>
|
|
<Typography variant="body2">{currentRecord?.receiver}</Typography>
|
|
</Box>
|
|
<Box display="flex" flexDirection="row" justifyContent="space-between">
|
|
<Typography variant="body2">Sent Amount:</Typography>
|
|
<Typography variant="body2">{formatCurrency(
|
|
new DecimalBigNumber(
|
|
BigInt(currentRecord ? currentRecord.amount : "0"),
|
|
18
|
|
).toString(), 9, ghstSymbol)
|
|
}</Typography>
|
|
</Box>
|
|
<Box display="flex" flexDirection="row" justifyContent="space-between">
|
|
<Typography variant="body2">Executed at:</Typography>
|
|
<Typography variant="body2">{
|
|
new Date(currentRecord ? currentRecord.timestamp : 0).toLocaleString('en-US')
|
|
}</Typography>
|
|
</Box>
|
|
</Box>
|
|
|
|
|
|
<Box display="flex" flexDirection="column" gap="5px">
|
|
<PrimaryButton
|
|
fullWidth
|
|
disabled={address === ""}
|
|
loading={false}
|
|
onClick={() => removeStoredRecord()}
|
|
>
|
|
Erase Record
|
|
</PrimaryButton>
|
|
|
|
<Typography variant="body2" sx={{ fontStyle: "italic" }}>
|
|
This will remove the transaction record from the session storage, but it will not cancel the bridge transaction.
|
|
</Typography>
|
|
</Box>
|
|
</Box>
|
|
</Modal>
|
|
<Box width="100%" maxWidth="476px" display="flex" alignItems="center" justifyContent="center" flexDirection="column">
|
|
<Paper
|
|
headerContent={
|
|
<Box alignItems="center" justifyContent="space-between" display="flex" width="100%">
|
|
<Typography variant="h4">{
|
|
bridgeAction
|
|
? `Bridge $${ghstSymbol}`
|
|
: "Transaction history"
|
|
}</Typography>
|
|
</Box>
|
|
}
|
|
topRight={
|
|
<GhostStyledIcon
|
|
component={bridgeAction ? PendingActionsIcon : PublicIcon}
|
|
viewBox="0 0 23 23"
|
|
style={{ display: "flex", alignItems: "center", cursor: "pointer" }}
|
|
onClick={() => setBridgeAction(!bridgeAction)}
|
|
/>
|
|
}
|
|
|
|
enableBackground
|
|
fullWidth
|
|
>
|
|
<Box minHeight="300px" display="flex" flexDirection="column" gap="5px">
|
|
{bridgeAction && (
|
|
<>
|
|
<SwapCollection
|
|
iconNotNeeded
|
|
UpperSwapCard={<SwapCard
|
|
id={`bridge-token-receiver`}
|
|
inputWidth={isVerySmallScreen ? "100px" : isSemiSmallScreen ? "180px" : "250px"}
|
|
value={receiver}
|
|
onChange={event => setReceiver(event.currentTarget.value)}
|
|
inputProps={{ "data-testid": "fromInput" }}
|
|
placeholder="Ghost address (sf prefixed)"
|
|
type="text"
|
|
/>}
|
|
LowerSwapCard={<SwapCard
|
|
id={`bridge-token-amount`}
|
|
inputWidth={isVerySmallScreen ? "100px" : isSemiSmallScreen ? "180px" : "250px"}
|
|
info={`${formatCurrency(ghstBalance.toString(), 4, ghstSymbol)}`}
|
|
value={amount}
|
|
onChange={event => setAmount(event.currentTarget.value)}
|
|
inputProps={{ "data-testid": "fromInput" }}
|
|
endString={"Max"}
|
|
endStringOnClick={() => setAmount(ghstBalance.toString())}
|
|
/>}
|
|
/>
|
|
<Box
|
|
mb="20px"
|
|
mt="20px"
|
|
flexDirection="column"
|
|
display="flex"
|
|
gap="10px"
|
|
justifyContent="space-between"
|
|
>
|
|
<Box display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
|
|
{gatekeeperAddressEmpty && (
|
|
<Box maxWidth="416px" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
|
|
<Typography mr="10px" variant="body2" color="textSecondary">
|
|
<em>
|
|
There is no connected gatekeeper on {chainName} network. Propose gatekeeper on this network to make validators listen to it.
|
|
</em>
|
|
</Typography>
|
|
</Box>
|
|
)}
|
|
{!gatekeeperAddressEmpty && (
|
|
<>
|
|
{!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">Gatekeeper:</Typography>}
|
|
<Link
|
|
fontSize="12px"
|
|
lineHeight="15px"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
href={`${chainExplorerUrl}/token/${gatekeeperAddress}`}
|
|
>
|
|
{gatekeeperAddress.slice(0, 10) + "..." + gatekeeperAddress.slice(-8)}
|
|
</Link>
|
|
</>
|
|
)}
|
|
</Box>
|
|
{incomingCommission && validators?.length ? (
|
|
<Box maxWidth="416px" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
|
|
<Typography mr="10px" variant="body2" color="textSecondary">
|
|
<em>
|
|
GHOST Wallet is not detected on your browser. Download
|
|
<Link
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
href="https://git.ghostchain.io/ghostchain/ghost-extension-wallet/releases"
|
|
>
|
|
GHOST Wallet
|
|
</Link> to see full detalization for bridge transaction.
|
|
</em>
|
|
</Typography>
|
|
</Box>
|
|
)
|
|
: (
|
|
<Box display="flex" flexDirection="column" gap="0px">
|
|
<Box maxWidth="416px" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
|
|
{!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">Est. Commission:</Typography>}
|
|
<Typography fontSize="12px" lineHeight="15px">unknown</Typography>
|
|
</Box>
|
|
<Box maxWidth="416px" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
|
|
{!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">Number of validators:</Typography>}
|
|
<Typography fontSize="12px" lineHeight="15px">unknown</Typography>
|
|
</Box>
|
|
</Box>
|
|
)}
|
|
|
|
</Box>
|
|
<PrimaryButton
|
|
fullWidth
|
|
disabled={
|
|
isPending || address === "" || gatekeeperAddressEmpty || !convertedReceiver ||
|
|
preparedAmount === 0n || ghstBalance._value < preparedAmount
|
|
}
|
|
loading={isPending}
|
|
onClick={() => ghostOrConnect()}
|
|
>
|
|
{address === "" ?
|
|
"Connect"
|
|
:
|
|
"Bridge"
|
|
}
|
|
</PrimaryButton>
|
|
</>
|
|
)}
|
|
{!bridgeAction && (
|
|
<Box>
|
|
<Box display="grid" gridTemplateColumns="1fr 1fr 60px" sx={{ padding: "0.6rem", borderBottom: "1px solid" }}>
|
|
<Typography variant="subtitle1" sx={{ fontWeight: "bold" }}>Amount</Typography>
|
|
<Typography variant="subtitle1" sx={{ fontWeight: "bold" }}>Datetime</Typography>
|
|
<Typography variant="subtitle1" sx={{ justifySelf: "center", fontWeight: "bold" }}>Status</Typography>
|
|
</Box>
|
|
<Box display="flex" flexDirection="column" sx={{
|
|
height: "250px",
|
|
overflowY: "scroll",
|
|
"-ms-overflow-style": "thin !important",
|
|
"scrollbar-width": "thin !important",
|
|
}}>
|
|
{filteredStoredTransactions
|
|
.map((obj, idx) => (
|
|
<Box
|
|
display="grid"
|
|
gridTemplateColumns="1fr 1fr 60px"
|
|
sx={{
|
|
borderBottom: "1px solid",
|
|
padding: "0.6rem",
|
|
paddingTop: "1.2rem",
|
|
cursor: 'pointer',
|
|
'&:hover': {
|
|
background: theme.colors.paper.cardHover
|
|
}
|
|
}}
|
|
key={obj.transactionHash}
|
|
onClick={() => setActiveTxIndex(idx)}
|
|
>
|
|
<Box display="flex" flexDirection="column" justifyContent="center">
|
|
<Typography variant="subtitle2">
|
|
{formatCurrency(
|
|
new DecimalBigNumber(BigInt(obj.amount), 18).toString(),
|
|
3,
|
|
ghstSymbol
|
|
)}
|
|
</Typography>
|
|
</Box>
|
|
|
|
<Box display="flex" flexDirection="column">
|
|
<Typography variant="subtitle2">
|
|
{new Date(obj.timestamp).toLocaleDateString('en-US')}
|
|
</Typography>
|
|
<Typography variant="subtitle2">
|
|
{new Date(obj.timestamp).toLocaleTimeString('en-US')}
|
|
</Typography>
|
|
</Box>
|
|
|
|
<Box display="flex" justifyContent="center" alignItems="center">
|
|
<Box
|
|
display="flex"
|
|
justifyContent="center"
|
|
alignItems="center"
|
|
sx={{
|
|
width: "25px",
|
|
height: "25px",
|
|
background: idx % 2 === 0
|
|
? theme.colors.feedback.warning
|
|
: theme.colors.feedback.success,
|
|
borderRadius: "100%",
|
|
boxShadow: "0px 0px 1px black"
|
|
}}
|
|
>
|
|
{idx % 2 === 0 ?
|
|
<GhostStyledIcon
|
|
sx={{ width: "20px", height: "20px" }}
|
|
viewBox="0 0 25 25"
|
|
component={HourglassBottomIcon}
|
|
/>
|
|
:
|
|
<GhostStyledIcon
|
|
sx={{ width: "20px", height: "20px" }}
|
|
viewBox="0 0 25 25"
|
|
component={CheckIcon}
|
|
/>
|
|
}
|
|
</Box>
|
|
</Box>
|
|
</Box>
|
|
))}
|
|
</Box>
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
</Paper>
|
|
</Box>
|
|
</Container>
|
|
</Box>
|
|
)
|
|
}
|
|
|
|
export default Bridge;
|