diff --git a/package.json b/package.json index 7e0c8a4..c1b5fc7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ghost-dao-interface", "private": true, - "version": "0.7.36", + "version": "0.7.37", "type": "module", "scripts": { "dev": "vite", diff --git a/src/abi/GhostStaking.json b/src/abi/GhostStaking.json index 7754d2c..1afc11e 100644 --- a/src/abi/GhostStaking.json +++ b/src/abi/GhostStaking.json @@ -1 +1 @@ -{"abi":[{"inputs":[{"internalType":"address","name":"_ftso","type":"address"},{"internalType":"address","name":"_stnk","type":"address"},{"internalType":"address","name":"_ghst","type":"address"},{"internalType":"uint48","name":"_epochLength","type":"uint48"},{"internalType":"uint48","name":"_firstEpochNumber","type":"uint48"},{"internalType":"uint48","name":"_firstEpochTime","type":"uint48"},{"internalType":"address","name":"_authority","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ExternalClaimsLocked","type":"error"},{"inputs":[],"name":"ExternalDepositsLocked","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"authority","type":"address"}],"name":"AuthorityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"distributor","type":"address"}],"name":"DistributorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"gatekeeper","type":"address"}],"name":"GatekeeperSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"warmup","type":"uint256"}],"name":"WarmupSet","type":"event"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract IGhostAuthority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"isRebase","type":"bool"}],"name":"claim","outputs":[{"internalType":"uint256","name":"claimedAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint48","name":"length","type":"uint48"},{"internalType":"uint48","name":"number","type":"uint48"},{"internalType":"uint48","name":"end","type":"uint48"},{"internalType":"uint256","name":"distribute","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"forfeit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ftso","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gatekeeper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"receiver","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ghost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ghostedSupply","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ghst","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"index","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"rx","type":"uint256"},{"internalType":"uint256","name":"s","type":"uint256"}],"name":"materialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rebase","outputs":[{"internalType":"uint256","name":"bounty","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAuthority","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_distributor","type":"address"}],"name":"setDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_gatekeeper","type":"address"}],"name":"setGatekeeperAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_warmupPeriod","type":"uint256"}],"name":"setWarmupPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sharesInWarmup","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"isRebase","type":"bool"},{"internalType":"bool","name":"isClaim","type":"bool"}],"name":"stake","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stnk","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supplyInWarmup","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"toggleLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"isTrigger","type":"bool"},{"internalType":"bool","name":"isRebase","type":"bool"}],"name":"unstake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"unwrap","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"warmupInfo","outputs":[{"internalType":"uint256","name":"deposit","type":"uint256"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint48","name":"expiry","type":"uint48"},{"internalType":"bool","name":"lock","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"warmupPeriod","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"wrap","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]} +{"abi":[{"inputs":[{"internalType":"address","name":"_ftso","type":"address"},{"internalType":"address","name":"_stnk","type":"address"},{"internalType":"address","name":"_ghst","type":"address"},{"internalType":"uint48","name":"_epochLength","type":"uint48"},{"internalType":"uint48","name":"_firstEpochNumber","type":"uint48"},{"internalType":"uint48","name":"_firstEpochTime","type":"uint48"},{"internalType":"address","name":"_authority","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ExternalClaimsLocked","type":"error"},{"inputs":[],"name":"ExternalDepositsLocked","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"authority","type":"address"}],"name":"AuthorityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"distributor","type":"address"}],"name":"DistributorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"gatekeeper","type":"address"}],"name":"GatekeeperSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isRebase","type":"bool"},{"indexed":false,"internalType":"bool","name":"isClaim","type":"bool"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isTrigger","type":"bool"},{"indexed":false,"internalType":"bool","name":"isRebase","type":"bool"}],"name":"Unstaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"warmup","type":"uint256"}],"name":"WarmupSet","type":"event"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract IGhostAuthority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"receiver","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"breakout","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"isRebase","type":"bool"}],"name":"claim","outputs":[{"internalType":"uint256","name":"claimedAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint48","name":"length","type":"uint48"},{"internalType":"uint48","name":"number","type":"uint48"},{"internalType":"uint48","name":"end","type":"uint48"},{"internalType":"uint256","name":"distribute","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"forfeit","outputs":[{"internalType":"uint256","name":"deposit","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ftso","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gatekeeper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"receiver","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ghost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ghostedSupply","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ghst","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"index","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"locks","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"rx","type":"uint256"},{"internalType":"uint256","name":"s","type":"uint256"}],"name":"materialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rebase","outputs":[{"internalType":"uint256","name":"bounty","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAuthority","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_distributor","type":"address"}],"name":"setDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_gatekeeper","type":"address"}],"name":"setGatekeeperAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_warmupPeriod","type":"uint256"}],"name":"setWarmupPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"isRebase","type":"bool"},{"internalType":"bool","name":"isClaim","type":"bool"}],"name":"stake","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stnk","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supplyInWarmup","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"toggleLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"isTrigger","type":"bool"},{"internalType":"bool","name":"isRebase","type":"bool"}],"name":"unstake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"unwrap","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"warmup","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"}],"name":"warmupInfo","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint48","name":"","type":"uint48"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"warmupPeriod","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"wrap","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]} diff --git a/src/containers/Breakout/BreakoutModal.jsx b/src/containers/Breakout/BreakoutModal.jsx index 964baa4..85c5a97 100644 --- a/src/containers/Breakout/BreakoutModal.jsx +++ b/src/containers/Breakout/BreakoutModal.jsx @@ -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 ( { estimatedAmount={estimatedAmount} incomingFee={incomingFee} goNext={() => setStep(2)} + convertedReceiver={convertedReceiver} + setConvertedReceiver={setConvertedReceiver} + /> : { setActiveTxIndex={setActiveTxIndex} closeModal={closeModalPure} evmNetwork={evmNetwork} + convertedReceiver={convertedReceiver} /> } @@ -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 ( <> Bridge to start earning {bridgeNumbers} {"Stake\u00B2"} on your {ghstSymbol} balance: @@ -283,14 +290,17 @@ const WelcomeView = ({ {formatNumber(apyInner, 2)}% APY - {warmupPeriod <= 0 && callDefaultFunction()} disabled={isPending || warmupPeriod > 0} loading={isPending} fullWidth > - {`${isPending ? "Claiming..." : "Claim"} ${isStakingOpened ? "(3, 3) Stake" : "(1, 1) Bond"}`} - } + {warmupPeriod > 0 + ? `Warm-up ends in ${prettifySecondsInDays(epoch.length * warmupPeriod)}` + : `${isPending ? "Claiming..." : "Claim"} ${isStakingOpened ? "(3, 3) Stake" : "(1, 1) Bond"}` + } +
@@ -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 ( <> diff --git a/src/containers/Bridge/Bridge.jsx b/src/containers/Bridge/Bridge.jsx index 2b73e2b..0a865da 100644 --- a/src/containers/Bridge/Bridge.jsx +++ b/src/containers/Bridge/Bridge.jsx @@ -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); diff --git a/src/containers/Bridge/BridgeCard.jsx b/src/containers/Bridge/BridgeCard.jsx index 665ba71..6ebeb52 100644 --- a/src/containers/Bridge/BridgeCard.jsx +++ b/src/containers/Bridge/BridgeCard.jsx @@ -307,7 +307,7 @@ export const BridgeCardHistory = ({ {formatCurrency( - new DecimalBigNumber(BigInt(obj.amount), 18).toString(), + new DecimalBigNumber(obj.amount, 18).toString(), isSemiSmallScreen ? 3 : 8, ghstSymbol )} diff --git a/src/containers/Stake/components/ClaimsArea.jsx b/src/containers/Stake/components/ClaimsArea.jsx index b4ff765..702dc3d 100644 --- a/src/containers/Stake/components/ClaimsArea.jsx +++ b/src/containers/Stake/components/ClaimsArea.jsx @@ -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 ( @@ -211,6 +221,7 @@ const ClaimInfo = ({ @@ -228,6 +239,7 @@ const MobileClaimInfo = ({ ghstSymbol, stnkSymbol, tokenPrice, + isLegacy, }) => { return ( @@ -257,12 +269,13 @@ const MobileClaimInfo = ({ isSmallScreen={true} setConfirmationModalOpen={setConfirmationModalOpen} isClaimable={isClaimable} + isLegacy={isLegacy} /> ); }; -const ActionButtons = ({ setConfirmationModalOpen, isSmallScreen = false, isClaimable = false }) => { +const ActionButtons = ({ setConfirmationModalOpen, isLegacy, isSmallScreen = false, isClaimable = false }) => { return ( setConfirmationModalOpen(true)} - disabled={isClaimable} + onClick={() => setConfirmationModalOpen(false)} + disabled={isLegacy && isClaimable} > Claim diff --git a/src/hooks/helpers.js b/src/hooks/helpers.js index 9139bb1..aea87d4 100644 --- a/src/hooks/helpers.js +++ b/src/hooks/helpers.js @@ -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), diff --git a/src/hooks/staking/index.js b/src/hooks/staking/index.js index f6cb891..96c207b 100644 --- a/src/hooks/staking/index.js +++ b/src/hooks/staking/index.js @@ -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({