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;