import { useMemo, useState, useEffect } from "react"; import { Box, Typography, Link, Skeleton, TableContainer, Table, Paper, TableHead, TableBody, TableRow, TableCell, Modal, useTheme, useMediaQuery, } from "@mui/material"; import { useConfig } from "wagmi"; import { ss58Decode } from "@polkadot-labs/hdkd-helpers"; import { toHex } from "@polkadot-api/utils"; import { PrimaryButton } from "../../components/Button"; import GhostStyledIcon from "../../components/Icon/GhostIcon"; import SwapCard from "../../components/Swap/SwapCard"; import SwapCollection from "../../components/Swap/SwapCollection"; import { ghost } from "../../hooks/staking"; import { useBalance } from "../../hooks/tokens"; import CheckIcon from '@mui/icons-material/Check'; import HourglassBottomIcon from '@mui/icons-material/HourglassBottom'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import { DecimalBigNumber } from "../../helpers/DecimalBigNumber"; import { formatNumber, formatCurrency, timeConverter } from "../../helpers"; import { BridgeRoute } from "./BridgeRoute"; const sliceString = (string, first, second) => { if (!string) return ""; return string.slice(0, first) + "..." + string.slice(second); } export const BridgeCardAction = ({ isVerySmallScreen, isSemiSmallScreen, chainId, address, ghstSymbol, gatekeeperAddressEmpty, gatekeeperAddress, evmNetwork, connect, isConfirmed, setIsConfirmed, openBridgeModal, storeTransactionHash, }) => { const [isPending, setIsPending] = useState(false); const [receiver, setReceiver] = useState(""); const [convertedReceiver, setConvertedReceiver] = useState(undefined); const [amount, setAmount] = useState(""); const config = useConfig(); const incomingFee = Number(evmNetwork?.incoming_fee ?? 0n) / 10000000; const { balance: ghstBalance, refetch: ghstBalanceRefetch } = useBalance(chainId, "GHST", address); const chainName = useMemo(() => { const client = config?.getClient(); return client?.chain?.name; }, [config]); const chainNativeCurrency = useMemo(() => { const client = config?.getClient(); return client?.chain?.nativeCurrency?.symbol; }, [config]); const chainExplorerUrl = useMemo(() => { const client = config?.getClient(); return client?.chain?.blockExplorers?.default?.url; }, [config]); 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 amountAfterFee = useMemo(() => { const convertedAmount = parseFloat(amount); if (!convertedAmount) { return 0; } return convertedAmount * (1 - incomingFee / 100); }, [amount, incomingFee]); const isDisabled = useMemo(() => { let isDisabled = isPending || gatekeeperAddressEmpty; if (address !== "") { isDisabled = isDisabled || !convertedReceiver || preparedAmount === 0n || ghstBalance._value < preparedAmount; } return isDisabled; }, [isPending, gatekeeperAddressEmpty, address, convertedReceiver, preparedAmount, ghstBalance]); const ghostFunds = async () => { setIsPending(true); try { const txHash = await ghost(chainId, address, convertedReceiver, preparedAmount); if (txHash) { storeTransactionHash(txHash, receiver, preparedAmount.toString()); } } finally { await ghstBalanceRefetch(); setReceiver(""); setAmount(""); setIsPending(false); } } useEffect(() => { if (isConfirmed) { setIsConfirmed(false); ghostFunds(); } }, [isConfirmed]); const ghostOrConnect = async () => { if (address === "") { connect(); } else if (!isConfirmed) { openBridgeModal(); } } useEffect(() => { try { const [publicKey, prefix] = ss58Decode(receiver); if (prefix !== 1995 && prefix !== 1996) { throw new Error("bad prefix"); } setConvertedReceiver(toHex(publicKey)); } catch { setConvertedReceiver(undefined); } }, [receiver]) return ( setReceiver(convertedReceiver ? "" : event.currentTarget.value)} inputProps={{ "data-testid": "fromInput" }} placeholder="GHOST address (sf prefixed)" endString={convertedReceiver ? : undefined } type="text" maxWidth="100%" />} LowerSwapCard={ setAmount(event.currentTarget.value)} inputProps={{ "data-testid": "fromInput" }} endString={"Max"} endStringOnClick={() => setAmount(ghstBalance.toString())} maxWidth="100%" />} /> {gatekeeperAddressEmpty && ( There is no connected gatekeeper on {chainName} network yet. )} {!gatekeeperAddressEmpty && ( {!isVerySmallScreen && Gatekeeper:} {sliceString(gatekeeperAddress, 10, -8)} )} {!isVerySmallScreen && Bridge Fee:} {incomingFee ? {`${incomingFee.toFixed(4)}%`} : } {!isVerySmallScreen && You will get:} {incomingFee ? {amountAfterFee.toFixed(4)} {ghstSymbol} : } ghostOrConnect()} > {address === "" ? "Connect" : isPending ? "Bridging..." : "Bridge" } ) } export const BridgeCardHistory = ({ isSemiSmallScreen, isBigScreen, filteredStoredTransactions, ghstSymbol, blockNumber, finalityDelay, setActiveTxIndex }) => { const isVeryBigScreen = useMediaQuery("(max-width: 1360px)"); const theme = useTheme(); const background = (index) => { return index % 2 === 1 ? "" : theme.colors.gray[750]; } return ( {!(isBigScreen ^ isVeryBigScreen) && Transaction } Datetime Status {filteredStoredTransactions?.map((obj, idx) => ( setActiveTxIndex(idx)} > {!(isBigScreen ^ isVeryBigScreen) && {formatCurrency( new DecimalBigNumber(BigInt(obj.amount), 18).toString(), isSemiSmallScreen ? 3 : 8, ghstSymbol )} {sliceString( obj.receiverAddress, isSemiSmallScreen ? 5 : 10, isSemiSmallScreen ? -3 : -8 )} } {new Date(obj.timestamp).toLocaleDateString('en-US')} {new Date(obj.timestamp).toLocaleTimeString('en-US')} {Number(blockNumber) - (obj.blockNumber + finalityDelay) < 0 ? : } ))}
) }