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 ( TX Hash  {currentRecord?.transactionHash.slice(0, 9)}...{currentRecord?.transactionHash.slice(-9)} } open={activeTxIndex > -1} onClose={() => setActiveTxIndex(-1)} minHeight={"100px"} > handleMouseEnter(0)} > Finalization {blockNumber?.toString()} blocks left handleMouseEnter(1)} > Slow claps {clappedValidators} / {validators.length} handleMouseEnter(2)} > Applaused Check Receiver Session Index: {currentRecord?.sessionIndex} Receiver Address: {currentRecord?.receiver} Sent Amount: {formatCurrency( new DecimalBigNumber( BigInt(currentRecord ? currentRecord.amount : "0"), 18 ).toString(), 9, ghstSymbol) } Executed at: { new Date(currentRecord ? currentRecord.timestamp : 0).toLocaleString('en-US') } removeStoredRecord()} > Erase Record This will remove the transaction record from the session storage, but it will not cancel the bridge transaction. { bridgeAction ? `Bridge $${ghstSymbol}` : "Transaction history" } } topRight={ setBridgeAction(!bridgeAction)} /> } enableBackground fullWidth > {bridgeAction && ( <> setReceiver(event.currentTarget.value)} inputProps={{ "data-testid": "fromInput" }} placeholder="Ghost address (sf prefixed)" type="text" />} LowerSwapCard={ setAmount(event.currentTarget.value)} inputProps={{ "data-testid": "fromInput" }} endString={"Max"} endStringOnClick={() => setAmount(ghstBalance.toString())} />} /> {gatekeeperAddressEmpty && ( There is no connected gatekeeper on {chainName} network. Propose gatekeeper on this network to make validators listen to it. )} {!gatekeeperAddressEmpty && ( <> {!isVerySmallScreen && Gatekeeper:} {gatekeeperAddress.slice(0, 10) + "..." + gatekeeperAddress.slice(-8)} )} {incomingCommission && validators?.length ? ( GHOST Wallet is not detected on your browser. Download  GHOST Wallet   to see full detalization for bridge transaction. ) : ( {!isVerySmallScreen && Est. Commission:} unknown {!isVerySmallScreen && Number of validators:} unknown )} ghostOrConnect()} > {address === "" ? "Connect" : "Bridge" } )} {!bridgeAction && ( Amount Datetime Status {filteredStoredTransactions .map((obj, idx) => ( setActiveTxIndex(idx)} > {formatCurrency( new DecimalBigNumber(BigInt(obj.amount), 18).toString(), 3, ghstSymbol )} {new Date(obj.timestamp).toLocaleDateString('en-US')} {new Date(obj.timestamp).toLocaleTimeString('en-US')} {idx % 2 === 0 ? : } ))} )} ) } export default Bridge;