make staking breakout works
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
parent
bc76372897
commit
fcc3d341d9
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ghost-dao-interface",
|
||||
"private": true,
|
||||
"version": "0.7.36",
|
||||
"version": "0.7.37",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -24,11 +24,13 @@ import { useTokenSymbol, useCirculatingSupply } from "../../hooks/tokens";
|
||||
import { useEpoch, useGatekeeperApy, useGatekeeperAddress } from "../../hooks/staking";
|
||||
import { useEvmNetwork, useCurrentIndex, useUnstableProvider } from "../../hooks/ghost";
|
||||
import { formatNumber, shorten } from "../../helpers";
|
||||
import { prettifySecondsInDays } from "../../helpers/timeUtil";
|
||||
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
||||
|
||||
const BreakoutModal = ({ chainId, address }) => {
|
||||
const [step, setStep] = useState(0);
|
||||
const [receiver, setReceiver] = useState("");
|
||||
const [convertedReceiver, setConvertedReceiver] = useState(undefined);
|
||||
|
||||
const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST");
|
||||
const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO");
|
||||
@ -78,6 +80,18 @@ const BreakoutModal = ({ chainId, address }) => {
|
||||
return `(${number}, ${number})`;
|
||||
}, [chainId]);
|
||||
|
||||
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 (
|
||||
<Modal
|
||||
headerContent={
|
||||
@ -113,6 +127,9 @@ const BreakoutModal = ({ chainId, address }) => {
|
||||
estimatedAmount={estimatedAmount}
|
||||
incomingFee={incomingFee}
|
||||
goNext={() => setStep(2)}
|
||||
convertedReceiver={convertedReceiver}
|
||||
setConvertedReceiver={setConvertedReceiver}
|
||||
|
||||
/>
|
||||
: <ConfirmStep
|
||||
chainId={chainId}
|
||||
@ -127,6 +144,7 @@ const BreakoutModal = ({ chainId, address }) => {
|
||||
setActiveTxIndex={setActiveTxIndex}
|
||||
closeModal={closeModalPure}
|
||||
evmNetwork={evmNetwork}
|
||||
convertedReceiver={convertedReceiver}
|
||||
/>
|
||||
}
|
||||
</Box>
|
||||
@ -138,6 +156,8 @@ const BridgeView = ({
|
||||
chainId,
|
||||
receiver,
|
||||
setReceiver,
|
||||
convertedReceiver,
|
||||
setConvertedReceiver,
|
||||
bridgeNumbers,
|
||||
ghstSymbol,
|
||||
estimatedAmount,
|
||||
@ -145,7 +165,6 @@ const BridgeView = ({
|
||||
incomingFee
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const [convertedReceiver, setConvertedReceiver] = useState(undefined);
|
||||
|
||||
const config = useConfig();
|
||||
const { gatekeeperAddress } = useGatekeeperAddress(chainId);
|
||||
@ -155,18 +174,6 @@ const BridgeView = ({
|
||||
return client?.chain?.blockExplorers?.default?.url;
|
||||
}, [config]);
|
||||
|
||||
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 (
|
||||
<>
|
||||
<Typography>Bridge to start earning {bridgeNumbers} {"Stake\u00B2"} on your {ghstSymbol} balance:</Typography>
|
||||
@ -283,14 +290,17 @@ const WelcomeView = ({
|
||||
<Typography variant="h5">{formatNumber(apyInner, 2)}% APY</Typography>
|
||||
</Box>
|
||||
|
||||
{warmupPeriod <= 0 && <SecondaryButton
|
||||
<SecondaryButton
|
||||
onClick={() => callDefaultFunction()}
|
||||
disabled={isPending || warmupPeriod > 0}
|
||||
loading={isPending}
|
||||
fullWidth
|
||||
>
|
||||
{`${isPending ? "Claiming..." : "Claim"} ${isStakingOpened ? "(3, 3) Stake" : "(1, 1) Bond"}`}
|
||||
</SecondaryButton>}
|
||||
{warmupPeriod > 0
|
||||
? `Warm-up ends in ${prettifySecondsInDays(epoch.length * warmupPeriod)}`
|
||||
: `${isPending ? "Claiming..." : "Claim"} ${isStakingOpened ? "(3, 3) Stake" : "(1, 1) Bond"}`
|
||||
}
|
||||
</SecondaryButton>
|
||||
|
||||
<Box display="flex" justifyContent="center" flexDirection="column" alignItems="center">
|
||||
<hr style={{ width: "100%" }} />
|
||||
@ -322,6 +332,7 @@ const ConfirmStep = ({
|
||||
chainId,
|
||||
address,
|
||||
receiver,
|
||||
convertedReceiver,
|
||||
executableFunction,
|
||||
ghstSymbol,
|
||||
bridgeNumbers,
|
||||
@ -360,7 +371,7 @@ const ConfirmStep = ({
|
||||
const execute = useCallback(async () => {
|
||||
setIsPending(true);
|
||||
try {
|
||||
const txHash = await executableFunction()(receiver);
|
||||
const txHash = await executableFunction()(convertedReceiver);
|
||||
if (txHash) {
|
||||
const expectedSessionIndex = (currentSession ?? 0) + (evmNetwork
|
||||
? Number((evmNetwork.avg_block_speed * evmNetwork.finality_delay) / (1000n * 14400n))
|
||||
@ -369,10 +380,7 @@ const ConfirmStep = ({
|
||||
const transaction = {
|
||||
sessionIndex: expectedSessionIndex,
|
||||
transactionHash: txHash,
|
||||
receiverAddress: receiver,
|
||||
amount: estimatedAmount._value.toString(),
|
||||
chainId: chainId,
|
||||
blockNumber: Number(blockNumber),
|
||||
bridgeStability: 69, // TODO: avoid stability
|
||||
timestamp: Date.now()
|
||||
}
|
||||
@ -388,7 +396,17 @@ const ConfirmStep = ({
|
||||
setIsPending(false);
|
||||
closeModal();
|
||||
}
|
||||
}, [executableFunction, receiver, networkName, chainId, address, blockNumber, evmNetwork, currentSession]);
|
||||
}, [
|
||||
executableFunction,
|
||||
convertedReceiver,
|
||||
receiver,
|
||||
networkName,
|
||||
chainId,
|
||||
address,
|
||||
blockNumber,
|
||||
evmNetwork,
|
||||
currentSession,
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useEffect, useState, useMemo, useCallback } from "react";
|
||||
import ReactGA from "react-ga4";
|
||||
import useSWR from "swr";
|
||||
|
||||
import {
|
||||
Box,
|
||||
@ -11,9 +12,9 @@ import {
|
||||
} from "@mui/material";
|
||||
import { decodeAddress } from "@polkadot/util-crypto";
|
||||
import { fromHex } from "@polkadot-api/utils";
|
||||
import { getBlockNumber } from "@wagmi/core";
|
||||
import { getBlockNumber, getTransaction } from "@wagmi/core";
|
||||
import { useTransaction } from "wagmi";
|
||||
import { keccak256 } from "viem";
|
||||
import { keccak256, decodeFunctionData } from "viem";
|
||||
import { u32, u64, u128 } from "scale-ts";
|
||||
|
||||
import PendingActionsIcon from '@mui/icons-material/PendingActions';
|
||||
@ -23,6 +24,7 @@ import PageTitle from "../../components/PageTitle/PageTitle";
|
||||
import Paper from "../../components/Paper/Paper";
|
||||
import GhostStyledIcon from "../../components/Icon/GhostIcon";
|
||||
|
||||
import { abi as StakingAbi } from "../../abi/GhostStaking.json";
|
||||
import { networkAvgBlockSpeed } from "../../constants";
|
||||
import { timeConverter } from "../../helpers";
|
||||
|
||||
@ -100,12 +102,25 @@ const Bridge = ({ chainId, address, config, connect }) => {
|
||||
hash: watchTransaction?.transactionHash
|
||||
});
|
||||
|
||||
const watchTransactionArgs = useMemo(() => {
|
||||
if (watchTransactionInfo && watchTransactionInfo.input) {
|
||||
const { functionName, args } = decodeFunctionData({
|
||||
abi: StakingAbi,
|
||||
data: watchTransactionInfo.input,
|
||||
});
|
||||
return { receiver: args.at(0), amount: args.at(1) }
|
||||
}
|
||||
return { receiver: "", amount: 0n }
|
||||
}, [watchTransactionInfo]);
|
||||
|
||||
const hashedArguments = useMemo(() => {
|
||||
if (!watchTransaction) return undefined
|
||||
if (!watchTransaction || !watchTransactionArgs.receiver) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const networkIdEncoded = u64.enc(BigInt(chainId));
|
||||
const amountEncoded = u128.enc(BigInt(watchTransaction.amount));
|
||||
const addressEncoded = decodeAddress(watchTransaction.receiverAddress, false, 1996);
|
||||
const amountEncoded = u128.enc(BigInt(watchTransactionArgs.amount));
|
||||
const addressEncoded = decodeAddress(watchTransactionArgs.receiver, false, 1996);
|
||||
const transactionHashEncoded = fromHex(watchTransaction.transactionHash);
|
||||
const blockNumber = u64.enc(watchTransactionInfo?.blockNumber ?? 0n);
|
||||
|
||||
@ -117,7 +132,7 @@ const Bridge = ({ chainId, address, config, connect }) => {
|
||||
...networkIdEncoded
|
||||
]);
|
||||
return keccak256(clapArgsStr)
|
||||
}, [watchTransaction, watchTransactionInfo])
|
||||
}, [watchTransaction, watchTransactionInfo, watchTransactionArgs])
|
||||
|
||||
const latestBlockNumber = useLatestBlockNumber();
|
||||
const eraIndex = useEraIndex();
|
||||
@ -183,7 +198,8 @@ const Bridge = ({ chainId, address, config, connect }) => {
|
||||
return sum + countOnesInBigInt(bigIntValue);
|
||||
}, 0);
|
||||
|
||||
const finalization = Math.max(0, (finalityDelay + watchTransaction.blockNumber) - Number(blockNumber));
|
||||
const storedBlockNumber = watchTransactionInfo ? Number(watchTransactionInfo.blockNumber) : 0;
|
||||
const finalization = Math.max(0, (finalityDelay + storedBlockNumber) - Number(blockNumber));
|
||||
const applaused = transactionApplaused?.finalized ?? false;
|
||||
const clappedAmount = transactionApplaused?.clapped_amount ?? 0n;
|
||||
const clappedPercentage = clappedAmount * 100n / (totalStakedAmount ?? 1n);
|
||||
@ -191,6 +207,8 @@ const Bridge = ({ chainId, address, config, connect }) => {
|
||||
|
||||
return {
|
||||
...watchTransaction,
|
||||
receiverAddress: watchTransactionArgs.receiver,
|
||||
amount: watchTransactionArgs.amount,
|
||||
finalization,
|
||||
applaused,
|
||||
numberOfClaps,
|
||||
@ -199,18 +217,49 @@ const Bridge = ({ chainId, address, config, connect }) => {
|
||||
clapsPercentage,
|
||||
}
|
||||
}, [
|
||||
watchTransaction,
|
||||
watchTransactionArgs,
|
||||
watchTransactionInfo,
|
||||
transactionApplaused,
|
||||
finalityDelay,
|
||||
watchTransaction,
|
||||
blockNumber,
|
||||
totalStakedAmount,
|
||||
authorities
|
||||
]);
|
||||
|
||||
const filteredStoredTransactions = useMemo(() => {
|
||||
const filteredStoredTransactionInfos = useMemo(() => {
|
||||
return storedTransactions.filter(obj => obj.chainId === chainId);
|
||||
}, [storedTransactions, chainId]);
|
||||
|
||||
const { data: filteredStoredTransactions } = useSWR(
|
||||
filteredStoredTransactionInfos.length > 0
|
||||
? ["filtered-tx", chainId, filteredStoredTransactionInfos.map(t => t.transactionHash)]
|
||||
: undefined,
|
||||
async ([,, hashes]) => {
|
||||
const results = await Promise.all(
|
||||
hashes.map(hash => getTransaction(config, { hash }).catch(() => undefined))
|
||||
);
|
||||
|
||||
return filteredStoredTransactionInfos.map((tx, index) => {
|
||||
const txInfo = results.at(index);
|
||||
let decodedData = { receiverAddress: "unknown", amount: 0n };
|
||||
|
||||
if (txInfo && txInfo.input) {
|
||||
const { args } = decodeFunctionData({
|
||||
abi: StakingAbi,
|
||||
data: txInfo.input,
|
||||
});
|
||||
if (args && args.at(0) && args.at(1)) {
|
||||
decodedData = { receiverAddress: args.at(0), amount: args.at(1) };
|
||||
}
|
||||
}
|
||||
|
||||
return { ...tx, ...decodedData };
|
||||
})
|
||||
},
|
||||
{ revalidateOnFocus: false }
|
||||
)
|
||||
|
||||
const latestCommits = useMemo(() => {
|
||||
return validators?.map((validator, index) => {
|
||||
const lastUpdatedNumber = Number(blockCommitments?.at(index)?.last_updated ?? 0);
|
||||
|
||||
@ -307,7 +307,7 @@ export const BridgeCardHistory = ({
|
||||
<Box display="flex" flexDirection="column" justifyContent="center">
|
||||
<Typography variant="caption">
|
||||
{formatCurrency(
|
||||
new DecimalBigNumber(BigInt(obj.amount), 18).toString(),
|
||||
new DecimalBigNumber(obj.amount, 18).toString(),
|
||||
isSemiSmallScreen ? 3 : 8,
|
||||
ghstSymbol
|
||||
)}
|
||||
|
||||
@ -76,9 +76,16 @@ export const ClaimsArea = ({ chainId, address, epoch }) => {
|
||||
return isPayoutGhst ? toClaim : toClaim.mul(currentIndex);
|
||||
}, [chainId, claim, currentIndex, balanceForShares]);
|
||||
|
||||
const breakoutBalance = useMemo(() => {
|
||||
if (isNetworkLegacy(chainId)) {
|
||||
return undefined; // short circuit
|
||||
}
|
||||
return isPayoutGhst ? claim.shares : claim.shares.mul(currentIndex);
|
||||
}, [chainId, claim, currentIndex]);
|
||||
|
||||
const setConfirmationModalOpen = useCallback(async (value) => {
|
||||
if (isNetworkLegacy(chainId) || claim.expiry > epoch.number) {
|
||||
setConfirmationModalOpenInner(value);
|
||||
if (isNetworkLegacy(chainId) || value) {
|
||||
setConfirmationModalOpenInner(true);
|
||||
} else {
|
||||
const defaultFunction = async () => {
|
||||
await claim(chainId, address, false, stnkSymbol, ghstSymbol);
|
||||
@ -86,22 +93,22 @@ export const ClaimsArea = ({ chainId, address, epoch }) => {
|
||||
}
|
||||
const warmupLeft = claim.expiry - epoch.number;
|
||||
const toExecute = async (receiver) => {
|
||||
await breakout(chainId, address, receiver, claimableBalance);
|
||||
await claimRefetch();
|
||||
const txHash = await breakout(chainId, address, receiver, breakoutBalance);
|
||||
return txHash;
|
||||
}
|
||||
breakoutFromStaking({ defaultFunction, toExecute, amount: claimableBalance, warmupLeft })
|
||||
}
|
||||
}, [claim, epoch, address, chainId, ghstSymbol]);
|
||||
}, [claim, epoch, address, chainId, ghstSymbol, breakoutBalance, claimableBalance]);
|
||||
|
||||
const closeConfirmationModal = () => {
|
||||
setConfirmationModalOpen(false);
|
||||
setConfirmationModalOpenInner(false);
|
||||
claimRefetch();
|
||||
currentIndexRefetch();
|
||||
}
|
||||
|
||||
if (claim.shares === 0n) return <></>;
|
||||
|
||||
const warmupTooltip = `Your claim earns rebases during warm-up. You can emergency withdraw, but this forfeits the rebases`;
|
||||
const warmupTooltip = `Your claim earns rebases during warm-up. You can emergency withdraw, but this forfeits the rebases`;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -141,6 +148,7 @@ const warmupTooltip = `Your claim earns rebases during warm-up. You can emergenc
|
||||
claim={claim}
|
||||
epoch={epoch}
|
||||
isClaimable={claim.expiry > epoch.number}
|
||||
isLegacy={isNetworkLegacy(chainId)}
|
||||
stnkSymbol={stnkSymbol}
|
||||
ghstSymbol={ghstSymbol}
|
||||
tokenPrice={isPayoutGhst ? ghstPrice : stnkPrice}
|
||||
@ -162,6 +170,7 @@ const warmupTooltip = `Your claim earns rebases during warm-up. You can emergenc
|
||||
claim={claim}
|
||||
epoch={epoch}
|
||||
isClaimable={claim.expiry > epoch.number}
|
||||
isLegacy={isNetworkLegacy(chainId)}
|
||||
stnkSymbol={stnkSymbol}
|
||||
ghstSymbol={ghstSymbol}
|
||||
tokenPrice={isPayoutGhst ? ghstPrice : stnkPrice}
|
||||
@ -183,7 +192,8 @@ const ClaimInfo = ({
|
||||
isPayoutGhst,
|
||||
stnkSymbol,
|
||||
ghstSymbol,
|
||||
tokenPrice
|
||||
tokenPrice,
|
||||
isLegacy,
|
||||
}) => {
|
||||
return (
|
||||
<TableBody>
|
||||
@ -211,6 +221,7 @@ const ClaimInfo = ({
|
||||
<ActionButtons
|
||||
setConfirmationModalOpen={setConfirmationModalOpen}
|
||||
isClaimable={isClaimable}
|
||||
isLegacy={isLegacy}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@ -228,6 +239,7 @@ const MobileClaimInfo = ({
|
||||
ghstSymbol,
|
||||
stnkSymbol,
|
||||
tokenPrice,
|
||||
isLegacy,
|
||||
}) => {
|
||||
return (
|
||||
<Box mt="10px">
|
||||
@ -257,12 +269,13 @@ const MobileClaimInfo = ({
|
||||
isSmallScreen={true}
|
||||
setConfirmationModalOpen={setConfirmationModalOpen}
|
||||
isClaimable={isClaimable}
|
||||
isLegacy={isLegacy}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const ActionButtons = ({ setConfirmationModalOpen, isSmallScreen = false, isClaimable = false }) => {
|
||||
const ActionButtons = ({ setConfirmationModalOpen, isLegacy, isSmallScreen = false, isClaimable = false }) => {
|
||||
return (
|
||||
<Box
|
||||
display="flex"
|
||||
@ -283,8 +296,8 @@ const ActionButtons = ({ setConfirmationModalOpen, isSmallScreen = false, isClai
|
||||
fullWidth={isSmallScreen}
|
||||
loading={false}
|
||||
sx={{ flexGrow: 1 }}
|
||||
onClick={() => setConfirmationModalOpen(true)}
|
||||
disabled={isClaimable}
|
||||
onClick={() => setConfirmationModalOpen(false)}
|
||||
disabled={isLegacy && isClaimable}
|
||||
>
|
||||
Claim
|
||||
</PrimaryButton>
|
||||
|
||||
@ -242,7 +242,7 @@ export const executeOnChainTransaction = async ({
|
||||
});
|
||||
|
||||
const finalRequest = sanitizeTransactionRequest(request, isLegacy);
|
||||
const txHash = await writeContract(config, finalRequest);
|
||||
const txHash = await writeContract(config, { ...finalRequest });
|
||||
await waitForTransactionReceipt(config, {
|
||||
hash: txHash,
|
||||
onReplaced: () => toast(messages.replacedMsg),
|
||||
|
||||
@ -226,7 +226,7 @@ export const breakout = async (chainId, account, receiver, amount) => {
|
||||
const args = [receiver, amount];
|
||||
const messages = {
|
||||
replacedMsg: "Breakout transaction was replaced. Wait for inclusion please.",
|
||||
successMsg: `Staking breakout succesfully bridged to ${shorten(receiver)}.`,
|
||||
successMsg: `Staking breakout succesfully done. Check tx hash status or wait for slow clap finalization.`,
|
||||
errorMsg: "Breakout transaction failed. Check logs for error detalization.",
|
||||
};
|
||||
const txHash = await executeOnChainTransaction({
|
||||
|
||||
Loading…
Reference in New Issue
Block a user