diff --git a/src/components/collator/join.tsx b/src/components/collator/join.tsx index ff18428..2df3ded 100644 --- a/src/components/collator/join.tsx +++ b/src/components/collator/join.tsx @@ -1,6 +1,7 @@ import { Button, Tooltip } from '@nextui-org/react'; import { CircleHelp } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; +import useCheckWaitingIndexing from '@/hooks/useWaitingIndexing'; import TransactionStatus from '../transaction-status'; import { useSetSessionKey } from './_hooks/set-session-key'; import { useCreateCollator, useCreateAndCollator } from './_hooks/collator'; @@ -21,7 +22,7 @@ const CollatorJoin = ({ hasSessionKey, sessionKey, hasPool, refetch }: CollatorJ const [commissionHash, setCommissionHash] = useState(''); const [sessionKeyValue, setSessionKeyValue] = useState(''); const [commissionValue, setCommissionValue] = useState(''); - + const { checkWaitingIndexing, isLoading: isLoadingWaitingIndexing } = useCheckWaitingIndexing(); const { setSessionKey, isPending: isPendingSetSessionKey } = useSetSessionKey(); const commission = useMemo(() => { @@ -84,6 +85,10 @@ const CollatorJoin = ({ hasSessionKey, sessionKey, hasPool, refetch }: CollatorJ }, []); const handleSetCommission = useCallback(async () => { + const { isDeployed } = await checkWaitingIndexing(); + if (!isDeployed) { + return; + } if (hasPool) { const tx = await createCollator({ commission @@ -103,7 +108,7 @@ const CollatorJoin = ({ hasSessionKey, sessionKey, hasPool, refetch }: CollatorJ setCommissionHash(tx); } } - }, [hasPool, commission, createCollator, createAndCollator]); + }, [hasPool, commission, createCollator, createAndCollator, checkWaitingIndexing]); const handleSetCommissionSuccess = useCallback(() => { setCommissionHash(''); @@ -239,7 +244,7 @@ const CollatorJoin = ({ hasSessionKey, sessionKey, hasPool, refetch }: CollatorJ className="h-[2.125rem] w-full" isDisabled={!hasSessionKey || !commissionValue || commission < 0n} onClick={handleSetCommission} - isLoading={isSetCommissionLoading || isLockPeriodLoading} + isLoading={isSetCommissionLoading || isLockPeriodLoading || isLoadingWaitingIndexing} > {hasPool ? 'Collate' : 'Create Nomination Pool & Collate'} diff --git a/src/components/collator/manage.tsx b/src/components/collator/manage.tsx index dbbc3bf..457d145 100644 --- a/src/components/collator/manage.tsx +++ b/src/components/collator/manage.tsx @@ -4,6 +4,7 @@ import { CircleHelp } from 'lucide-react'; import { validSessionKey } from '@/utils'; import { fetchCollatorSetPrev, useCollatorByAddress } from '@/hooks/useService'; import useWalletStatus from '@/hooks/useWalletStatus'; +import useCheckWaitingIndexing from '@/hooks/useWaitingIndexing'; import { DEFAULT_PREV } from '@/utils/getPrevNew'; import TransactionStatus from '../transaction-status'; import StopCollation from './stop-collation'; @@ -34,7 +35,7 @@ const CollatorManagement = ({ const [sessionKeyValue, setSessionKeyValue] = useState(''); const [commissionValue, setCommissionValue] = useState(''); const [isLoadingPrev, setIsLoadingPrev] = useState(false); - + const { checkWaitingIndexing, isLoading: isLoadingWaitingIndexing } = useCheckWaitingIndexing(); const { data: collatorByAddress, isLoading: isLoadingCollatorByAddress } = useCollatorByAddress({ address: address as `0x${string}`, enabled: true @@ -131,6 +132,10 @@ const CollatorManagement = ({ }, []); const handleSetCommission = useCallback(async () => { + const { isDeployed } = await checkWaitingIndexing(); + if (!isDeployed) { + return; + } const oldPrev = await getPrevAddress(); if (!oldPrev) { error('Previous key is missing. Please verify your collator information.'); @@ -142,7 +147,7 @@ const CollatorManagement = ({ if (tx) { setCommissionHash(tx); } - }, [updateCommission, getPrevAddress]); + }, [updateCommission, getPrevAddress, checkWaitingIndexing]); const handleSetCommissionSuccess = useCallback(() => { setCommissionHash(''); @@ -153,6 +158,10 @@ const CollatorManagement = ({ }, []); const handleStop = useCallback(async () => { + const { isDeployed } = await checkWaitingIndexing(); + if (!isDeployed) { + return; + } const oldPrev = await getPrevAddress(); if (!oldPrev) { error('Previous key is missing. Please verify your collator information.'); @@ -164,7 +173,7 @@ const CollatorManagement = ({ if (tx) { setStopHash(tx); } - }, [stop, getPrevAddress]); + }, [stop, getPrevAddress, checkWaitingIndexing]); const handleStopSuccess = useCallback(() => { setStopHash(''); @@ -272,7 +281,8 @@ const CollatorManagement = ({ isLockPeriodLoading || isLoadingUpdateCommission || isLoadingPrev || - isLoadingCollatorByAddress + isLoadingCollatorByAddress || + isLoadingWaitingIndexing } > Update Commission @@ -284,7 +294,7 @@ const CollatorManagement = ({ color="primary" className="h-[2.125rem] w-full" variant="light" - isLoading={isPendingStop || isLoadingPrev} + isLoading={isPendingStop || isLoadingPrev || isLoadingWaitingIndexing} onClick={handleStop} > Stop Collation diff --git a/src/components/deposit-item.tsx b/src/components/deposit-item.tsx index d027701..7346c5a 100644 --- a/src/components/deposit-item.tsx +++ b/src/components/deposit-item.tsx @@ -1,8 +1,8 @@ import { Checkbox, Progress, Tooltip } from '@nextui-org/react'; -import dayjs from 'dayjs'; import { formatEther } from 'viem'; import FormattedNumberTooltip from './formatted-number-tooltip'; import type { DepositInfo } from '@/hooks/useUserDepositDetails'; +import { calculateDepositProgress } from '@/utils/date'; interface DepositItemProps { item: DepositInfo; @@ -13,12 +13,12 @@ interface DepositItemProps { const DepositItem = ({ item, isChecked, symbol, onChange }: DepositItemProps) => { const value = formatEther(item?.value || 0n); - const startAtDate = dayjs(item?.startAt * 1000).format('YYYY-MM-DD'); - const endAtDate = dayjs(item?.endAt * 1000).format('YYYY-MM-DD'); - const now = dayjs().unix(); - const totalDuration = item?.endAt - item?.startAt; - const elapsedDuration = Math.max(0, Math.min(now - item?.startAt, totalDuration)); - const progressValue = (elapsedDuration / totalDuration) * 100; + + const { startAtDate, endAtDate, progressValue } = calculateDepositProgress( + item?.startAt, + item?.endAt + ); + return (
{ return (
-
+
empty

{label}

diff --git a/src/components/reserved-staking-panel.tsx b/src/components/reserved-staking-panel.tsx index af79941..fb26c8f 100644 --- a/src/components/reserved-staking-panel.tsx +++ b/src/components/reserved-staking-panel.tsx @@ -2,8 +2,10 @@ import { useStakingAccount } from '@/hooks/useService'; import TooltipFormattedNumber from './tooltip-formatter-number'; import { formatEther } from 'viem'; import { Skeleton } from '@nextui-org/react'; +import useWalletStatus from '@/hooks/useWalletStatus'; const ReservedStakingPanel = () => { + const { nativeTokenIcon, currentChain } = useWalletStatus(); const { data, isLoading } = useStakingAccount({ enabled: true }); @@ -14,7 +16,15 @@ const ReservedStakingPanel = () => { return (
- ring + {nativeTokenIcon ? ( + {currentChain?.nativeCurrency?.name} + ) : ( + ring + )}

Reserved in Staking

{isLoading ? ( diff --git a/src/components/unstake-deposit-item.tsx b/src/components/unstake-deposit-item.tsx index 741d90a..fcbf6c9 100644 --- a/src/components/unstake-deposit-item.tsx +++ b/src/components/unstake-deposit-item.tsx @@ -1,39 +1,48 @@ import { Checkbox, Progress, Tooltip } from '@nextui-org/react'; import { formatEther } from 'viem'; -import dayjs from 'dayjs'; import FormattedNumberTooltip from './formatted-number-tooltip'; import type { StakedDepositInfo } from '@/view/stake/_hooks/staked'; +import { calculateDepositProgress } from '@/utils/date'; interface UnstakeDepositItemProps { item: StakedDepositInfo; isChecked?: boolean; + isDisabled?: boolean; symbol?: string; onChange: (id: bigint) => void; } -const UnstakeDepositItem = ({ item, isChecked, symbol, onChange }: UnstakeDepositItemProps) => { +const UnstakeDepositItem = ({ + item, + isChecked, + isDisabled, + symbol, + onChange +}: UnstakeDepositItemProps) => { const value = formatEther(item?.amount || 0n); - const startAtDate = dayjs(item?.startAt * 1000).format('YYYY-MM-DD'); - const endAtDate = dayjs(item?.endAt * 1000).format('YYYY-MM-DD'); - const now = dayjs().unix(); - const totalDuration = item?.endAt - item?.startAt; - const elapsedDuration = Math.max(0, Math.min(now - item?.startAt, totalDuration)); - const progressValue = (elapsedDuration / totalDuration) * 100; + const { startAtDate, endAtDate, progressValue } = calculateDepositProgress( + item?.startAt, + item?.endAt + ); + return (
{ + if (isDisabled) return; onChange(item?.tokenId); }} > { + if (isDisabled) return; onChange(item?.tokenId); }} classNames={{ diff --git a/src/components/unstake-deposit-list.tsx b/src/components/unstake-deposit-list.tsx index 6b23787..f889809 100644 --- a/src/components/unstake-deposit-list.tsx +++ b/src/components/unstake-deposit-list.tsx @@ -9,13 +9,14 @@ export type DepositListRef = { reset: () => void; }; interface DepositListProps { + isDisabled?: boolean; maxCount?: number; deposits: StakedDepositInfo[]; onCheckedDepositsChange: (deposits: StakedDepositInfo[]) => void; } const UnstakeDepositList = forwardRef( - ({ maxCount = 5, deposits, onCheckedDepositsChange }, ref) => { + ({ maxCount = 5, deposits, onCheckedDepositsChange, isDisabled = false }, ref) => { const [checkedDeposits, setCheckedDeposits] = useState([]); const { chain } = useWalletStatus(); @@ -52,6 +53,7 @@ const UnstakeDepositList = forwardRef( handleDepositChange(deposit)} diff --git a/src/config/chains/index.ts b/src/config/chains/index.ts index c420d41..ca38604 100644 --- a/src/config/chains/index.ts +++ b/src/config/chains/index.ts @@ -1,4 +1,7 @@ import { ChainId } from '@/types/chains'; +import { nativeTokenIcon as crabNativeTokenIcon } from './crab'; +import { nativeTokenIcon as darwiniaNativeTokenIcon } from './darwinia'; +import { nativeTokenIcon as koiNativeTokenIcon } from './koi'; export { chain as crab, nativeTokenIcon as crabNativeTokenIcon } from './crab'; export { chain as darwinia, nativeTokenIcon as darwiniaNativeTokenIcon } from './darwinia'; @@ -10,6 +13,12 @@ export const KTON_TOKEN_MAP = new Map([ [ChainId.KOI, 'PKTON'] ]); +export const NATIVE_TOKEN_ICON_MAP = new Map([ + [ChainId.CRAB, crabNativeTokenIcon], + [ChainId.DARWINIA, darwiniaNativeTokenIcon], + [ChainId.KOI, koiNativeTokenIcon] +]); + export const KTON_TOKEN_INFO_MAP = new Map< ChainId, { symbol: string; decimals: number; address: `0x${string}` } diff --git a/src/config/tabs.ts b/src/config/tabs.ts index 674c066..d2c91e9 100644 --- a/src/config/tabs.ts +++ b/src/config/tabs.ts @@ -13,9 +13,9 @@ export const defiTabs = [ } ]; -export const stakeTabs = [ +export const stakeTabs = (tokenName: string) => [ { - label: 'Stake RING', + label: `Stake ${tokenName}`, key: 'stake-ring' }, { diff --git a/src/hooks/useService.ts b/src/hooks/useService.ts index 4262eeb..7927d6d 100644 --- a/src/hooks/useService.ts +++ b/src/hooks/useService.ts @@ -36,7 +36,7 @@ export function useCollatorSet({ }; return useQuery({ - queryKey: ['collatorSet', params], + queryKey: ['collatorSet', params, currentChainId], queryFn: async () => { const result = await fetchCollatorSet(params, currentChainId!); @@ -104,7 +104,7 @@ export function useCollatorSetNewPrev({ }; return useQuery({ - queryKey: ['collatorSetNewPrev', params], + queryKey: ['collatorSetNewPrev', params, currentChainId], queryFn: async () => { const result = await fetchCollatorSet(params, currentChainId!); if (result === null) { @@ -162,7 +162,7 @@ export function useCollatorByAddress({ address, enabled = true }: CollatorByAddr }; return useQuery({ - queryKey: ['collatorByAddress', params], + queryKey: ['collatorByAddress', params, currentChainId], queryFn: async () => { const result = await fetchCollatorSet(params, currentChainId!); if (result === null) { @@ -218,7 +218,7 @@ export function useCollatorSetByAccounts({ first: 1000 }; return useQuery({ - queryKey: ['collatorSetByAccounts', params], + queryKey: ['collatorSetByAccounts', params, currentChainId], queryFn: async () => { const result = await fetchCollatorSetByAccounts(params, currentChainId!); if (result === null) { @@ -243,7 +243,7 @@ export function useStakingAccount({ enabled = true }: StakingAccountParams) { orderBy: 'latestChangeTimestamp', orderDirection: 'desc' }; - const queryKey = ['stakingAccounts', params]; + const queryKey = ['stakingAccounts', params, currentChainId]; const result = useQuery({ queryKey, queryFn: async () => { diff --git a/src/hooks/useWaitingIndexing.ts b/src/hooks/useWaitingIndexing.ts new file mode 100644 index 0000000..d75f71d --- /dev/null +++ b/src/hooks/useWaitingIndexing.ts @@ -0,0 +1,55 @@ +import { useCallback, useState } from 'react'; +import { readContract } from '@wagmi/core'; +import { abi as hubAbi, address as hubAddress } from '@/config/abi/hub'; +import { fetchDeploymentMeta } from '@/service/services'; +import { config } from '@/config/wagmi'; +import { useWaitingIndexing } from '@/components/waiting-indexing/use-waiting-indexing'; +import useWalletStatus from './useWalletStatus'; + +function useCheckWaitingIndexing() { + const { currentChainId } = useWalletStatus(); + const { open } = useWaitingIndexing(); + const [isLoading, setIsLoading] = useState(false); + + const checkWaitingIndexing = useCallback(async () => { + if (!currentChainId) { + return { isDeployed: false, error: new Error('ChainId is not set') }; + } + setIsLoading(true); + try { + const [updateTimeStamp, deploymentMeta] = await Promise.all([ + readContract(config, { + address: hubAddress, + abi: hubAbi, + functionName: 'updateTimeStamp' + }), + fetchDeploymentMeta(currentChainId) + ]); + + if (updateTimeStamp && deploymentMeta?._meta?.block?.timestamp) { + const contractTimestamp = BigInt(updateTimeStamp.toString()); + const indexedTimestamp = BigInt(deploymentMeta._meta.block.timestamp); + console.log('contractTimestamp', contractTimestamp); + console.log('indexedTimestamp', indexedTimestamp); + const isDeployed = contractTimestamp <= indexedTimestamp; + if (!isDeployed) { + open(); + } + return { isDeployed, error: null }; + } + + return { isDeployed: false, error: null }; + } catch (error) { + return { + isDeployed: false, + error: error instanceof Error ? error : new Error('Unknown error') + }; + } finally { + setIsLoading(false); + } + }, [currentChainId, open]); + + return { checkWaitingIndexing, isLoading }; +} + +export default useCheckWaitingIndexing; diff --git a/src/hooks/useWalletStatus.ts b/src/hooks/useWalletStatus.ts index cfae3d3..4880256 100644 --- a/src/hooks/useWalletStatus.ts +++ b/src/hooks/useWalletStatus.ts @@ -4,6 +4,7 @@ import { getChainById, getChains } from '@/utils/chain'; import { GRING_TOKEN_ADDRESS_MAP, KTON_TOKEN_INFO_MAP, + NATIVE_TOKEN_ICON_MAP, RING_DAO_GOVERNANCE_MAP } from '@/config/chains'; import { ChainId } from '@/types/chains'; @@ -15,6 +16,7 @@ function useWalletStatus() { const isSupportedChain = result.chainId ? chainIds.includes(result.chainId) : false; const currentChainId = isSupportedChain ? (result.chainId as ChainId) : undefined; const currentChain = currentChainId ? getChainById(currentChainId) : undefined; + const nativeTokenIcon = NATIVE_TOKEN_ICON_MAP.get(currentChainId as ChainId); const ktonInfo = KTON_TOKEN_INFO_MAP.get(currentChainId as ChainId) ?? KTON_TOKEN_INFO_MAP.get(ChainId.DARWINIA); @@ -29,7 +31,8 @@ function useWalletStatus() { currentChain, ktonInfo, ringDAOGovernance, - gringTokenInfo + gringTokenInfo, + nativeTokenIcon }; } diff --git a/src/service/queries.ts b/src/service/queries.ts index fa8d88e..bbe9556 100644 --- a/src/service/queries.ts +++ b/src/service/queries.ts @@ -75,3 +75,18 @@ export const GET_STAKING_ACCOUNT = gql` } } `; + +export const GET_DEPLOYMENT_META = gql` + query GetDeploymentMeta { + _meta { + hasIndexingErrors + deployment + block { + timestamp + parentHash + number + hash + } + } + } +`; diff --git a/src/service/services.ts b/src/service/services.ts index c574378..e18e6ff 100644 --- a/src/service/services.ts +++ b/src/service/services.ts @@ -1,9 +1,15 @@ import { ChainId } from '@/types/chains'; import { getClient } from './client'; -import { GET_COLLATOR_SET, GET_COLLATOR_SET_BY_INSET, GET_STAKING_ACCOUNT } from './queries'; +import { + GET_COLLATOR_SET, + GET_COLLATOR_SET_BY_INSET, + GET_DEPLOYMENT_META, + GET_STAKING_ACCOUNT +} from './queries'; import type { CollatorSet, CollatorSetQueryParams, + DeploymentMetaResponse, StakingAccount, StakingAccountQueryParams } from './type'; @@ -58,3 +64,17 @@ export async function fetchStakingAccount( return null; } } + +export async function fetchDeploymentMeta( + chainId: ChainId +): Promise { + const client = getClient(chainId); + + try { + const data = await client.request(GET_DEPLOYMENT_META); + return data; + } catch (error) { + console.error('fetchDeploymentMeta failed:', error); + return null; + } +} diff --git a/src/service/type.ts b/src/service/type.ts index 7b6f520..d865f12 100644 --- a/src/service/type.ts +++ b/src/service/type.ts @@ -318,3 +318,16 @@ export interface StakingAccountQueryParams { orderDirection?: OrderDirection; where?: StakingAccount_filter; } + +export interface DeploymentMetaResponse { + _meta: { + hasIndexingErrors: boolean; + deployment: string; + block: { + timestamp: string; + parentHash: string; + number: string; + hash: string; + }; + }; +} diff --git a/src/utils/date.ts b/src/utils/date.ts index 335d3d8..6904d1e 100644 --- a/src/utils/date.ts +++ b/src/utils/date.ts @@ -5,4 +5,22 @@ import utc from 'dayjs/plugin/utc'; dayjs.extend(duration); dayjs.extend(utc); +export function calculateDepositProgress( + startAt: number, + endAt: number +): { + startAtDate: string; + endAtDate: string; + progressValue: number; +} { + const startAtDate = dayjs(startAt * 1000).format('YYYY-MM-DD'); + const endAtDate = dayjs(endAt * 1000).format('YYYY-MM-DD'); + const now = dayjs().unix(); + const totalDuration = endAt - startAt; + const elapsedDuration = Math.max(0, Math.min(now - startAt, totalDuration)); + const progressValue = (elapsedDuration / totalDuration) * 100; + + return { startAtDate, endAtDate, progressValue }; +} + export default dayjs; diff --git a/src/view/claim/_components/index.tsx b/src/view/claim/_components/index.tsx index 46ce014..75eb4e2 100644 --- a/src/view/claim/_components/index.tsx +++ b/src/view/claim/_components/index.tsx @@ -4,6 +4,7 @@ import useStakingAccountWithStatus, { StakingAccountWithStatus } from '@/hooks/useStakingAccountWithStatus'; import TransactionStatus from '@/components/transaction-status'; +import useCheckWaitingIndexing from '@/hooks/useWaitingIndexing'; import { error } from '@/components/toast'; import { abi as rewardAbi } from '@/config/abi/reward'; import { ClaimableReward } from './item'; @@ -12,7 +13,7 @@ import ClaimList from './list'; const Claim = () => { const [hash, setHash] = useState<`0x${string}` | undefined>(undefined); - + const { checkWaitingIndexing } = useCheckWaitingIndexing(); const { claim } = useClaim(); const { data: stakingAccount, isLoading: isStakingAccountLoading } = @@ -45,6 +46,10 @@ const Claim = () => { const handleClick = useCallback( async (item: StakingAccountWithStatus) => { + const { isDeployed } = await checkWaitingIndexing(); + if (!isDeployed) { + return; + } const tx = await claim(item?.collator as `0x${string}`)?.catch((e) => { error(e.shortMessage); }); @@ -52,7 +57,7 @@ const Claim = () => { setHash(tx); } }, - [claim] + [checkWaitingIndexing, claim] ); const handleSuccess = useCallback(() => { diff --git a/src/view/deposit/_components/index.tsx b/src/view/deposit/_components/index.tsx index 9dbbf63..04ec91d 100644 --- a/src/view/deposit/_components/index.tsx +++ b/src/view/deposit/_components/index.tsx @@ -112,7 +112,7 @@ const Deposit = () => { variant="light" onClick={() => setIsOpen(true)} > - Wallet Deposit + Deposit in Wallet
diff --git a/src/view/deposit/_components/records.tsx b/src/view/deposit/_components/records.tsx index f5b9e12..e245442 100644 --- a/src/view/deposit/_components/records.tsx +++ b/src/view/deposit/_components/records.tsx @@ -6,13 +6,15 @@ import { ModalContent, ModalHeader, Pagination, + Progress, Spinner, Table, TableBody, TableCell, TableColumn, TableHeader, - TableRow + TableRow, + Tooltip } from '@nextui-org/react'; import { X } from 'lucide-react'; import { Key, useCallback, useEffect, useState } from 'react'; @@ -29,6 +31,7 @@ import TransactionStatus from '@/components/transaction-status'; import AsyncButton from '@/components/async-button'; import { error } from '@/components/toast'; import FormattedNumberTooltip from '@/components/formatted-number-tooltip'; +import { calculateDepositProgress } from '@/utils/date'; const PAGE_SIZE = 10; interface DepositRecordsModalProps { @@ -134,19 +137,53 @@ const DepositRecordsModal = ({ case 'tokenId': return (
- Token ID [{cellValue.toString()}] + + Token ID [{cellValue.toString()}] +
); - case 'value': + case 'value': { + const { startAtDate, endAtDate, progressValue } = calculateDepositProgress( + item?.startAt, + item?.endAt + ); + return ( - - - {(formattedValue) => formattedValue} - - {currentChain?.nativeCurrency?.symbol} - + + +
+ + {(formattedValue) => ( + + {formattedValue} + + )} + + + + {currentChain?.nativeCurrency?.symbol} + +
+
+ } + value={progressValue} + className="w-full gap-1" + size="sm" + color="primary" + /> + ); + } case 'action': { if (item.isClaimRequirePenalty) { @@ -178,7 +215,12 @@ const DepositRecordsModal = ({ return null; } }, - [currentChain?.nativeCurrency?.symbol, handleWithdrawEarlier, handleWithdraw] + [ + currentChain?.nativeCurrency?.symbol, + currentChain?.blockExplorers?.default.url, + handleWithdrawEarlier, + handleWithdraw + ] ); const totalPages = Math.ceil((depositList?.length || 0) / PAGE_SIZE); const paginatedData = depositList?.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE); @@ -206,7 +248,7 @@ const DepositRecordsModal = ({ > - Wallet Deposit + Deposit in Wallet No active deposit records
} + emptyContent={
No deposit in wallet
} className="relative" loadingContent={
diff --git a/src/view/stake/_components/manage/index.tsx b/src/view/stake/_components/manage/index.tsx index 40124a0..b747306 100644 --- a/src/view/stake/_components/manage/index.tsx +++ b/src/view/stake/_components/manage/index.tsx @@ -142,21 +142,6 @@ const StakeManagementModal = ({ ); }, [handleUnstakeRingOpen, isLocked]); - const UnstakeDepositsButton = useMemo(() => { - return ( - - ); - }, [handleUnstakeDepositsOpen, isLocked]); - const renderDays = useMemo(() => { return ( @@ -175,6 +160,21 @@ const StakeManagementModal = ({ return '0'; }, [stakedDeposits]); + const UnstakeDepositsButton = useMemo(() => { + return ( + + ); + }, [handleUnstakeDepositsOpen, formattedStakedDeposits]); + useEffect(() => { if (isOpen) { refetchCollator(); @@ -274,24 +274,7 @@ const StakeManagementModal = ({ )}
- {isLocked ? ( - - You can perform the unstake operation {renderDays} after your last stake - with the selected collator. You have {renderDays} remaining before you - can unstake. -
- } - closeDelay={0} - color="default" - showArrow - > -
{UnstakeDepositsButton}
- - ) : ( - UnstakeDepositsButton - )} + {UnstakeDepositsButton} diff --git a/src/view/stake/_components/manage/unstake-deposts.tsx b/src/view/stake/_components/manage/unstake-deposts.tsx index 1a56c39..c17daf6 100644 --- a/src/view/stake/_components/manage/unstake-deposts.tsx +++ b/src/view/stake/_components/manage/unstake-deposts.tsx @@ -1,7 +1,15 @@ -import { Button, Divider, Modal, ModalBody, ModalContent, ModalHeader } from '@nextui-org/react'; +import { + Button, + Divider, + Modal, + ModalBody, + ModalContent, + ModalHeader, + Tooltip +} from '@nextui-org/react'; import { X } from 'lucide-react'; -import { useCallback, useRef, useState } from 'react'; +import { useCallback, useRef, useState, ReactNode } from 'react'; import type { StakedDepositInfo } from '../../_hooks/staked'; import UnstakeDepositList, { DepositListRef } from '@/components/unstake-deposit-list'; @@ -9,34 +17,49 @@ import TransactionStatus from '@/components/transaction-status'; import { useUnstakeDeposits } from '../../_hooks/unstake'; import { CollatorSet } from '@/service/type'; import { error } from '@/components/toast'; +import useCheckWaitingIndexing from '@/hooks/useWaitingIndexing'; interface EditStakeProps { isOpen: boolean; collator?: CollatorSet; symbol: string; deposits: StakedDepositInfo[]; + isLocked?: boolean; onClose: () => void; onOk: () => void; + renderDays?: ReactNode; } -const UnstakeDeposits = ({ isOpen, collator, deposits, onClose, onOk }: EditStakeProps) => { +const UnstakeDeposits = ({ + isOpen, + collator, + deposits, + isLocked, + renderDays, + onClose, + onOk +}: EditStakeProps) => { const depositListRef = useRef(null); const [checkedDeposits, setCheckedDeposits] = useState([]); const [hash, setHash] = useState<`0x${string}` | undefined>(undefined); - + const { checkWaitingIndexing, isLoading: isLoadingWaitingIndexing } = useCheckWaitingIndexing(); const { unstakeDeposits, isLoadingOldAndNewPrev, isPending } = useUnstakeDeposits({ collator, deposits: checkedDeposits }); const handleUnstakeStart = useCallback(async () => { + const { isDeployed } = await checkWaitingIndexing(); + if (!isDeployed) { + return; + } const tx = await unstakeDeposits()?.catch((e) => { error(e.shortMessage); }); if (tx) { setHash(tx); } - }, [unstakeDeposits]); + }, [checkWaitingIndexing, unstakeDeposits]); const handleSuccess = useCallback(() => { setHash(undefined); @@ -51,6 +74,7 @@ const UnstakeDeposits = ({ isOpen, collator, deposits, onClose, onOk }: EditStak <> - - + {isLocked ? ( + + You can perform the unstake operation {renderDays} after your last stake with + the selected collator. You have {renderDays} remaining before you can unstake. +
+ } + closeDelay={0} + color="default" + showArrow + > +
+ { + + } +
+ + ) : ( + + )} diff --git a/src/view/stake/_components/manage/unstake.tsx b/src/view/stake/_components/manage/unstake.tsx index 55137b4..37267a2 100644 --- a/src/view/stake/_components/manage/unstake.tsx +++ b/src/view/stake/_components/manage/unstake.tsx @@ -6,6 +6,7 @@ import AmountInputWithBalance from '@/components/amount-input-with-balance'; import TransactionStatus from '@/components/transaction-status'; import { error } from '@/components/toast'; import { useUnstakeRING } from '../../_hooks/unstake'; +import useCheckWaitingIndexing from '@/hooks/useWaitingIndexing'; import type { CollatorSet } from '@/service/type'; interface EditStakeProps { @@ -28,20 +29,24 @@ const Unstake = ({ }: EditStakeProps) => { const [hash, setHash] = useState<`0x${string}` | undefined>(undefined); const [amount, setAmount] = useState('0'); - + const { checkWaitingIndexing, isLoading: isLoadingWaitingIndexing } = useCheckWaitingIndexing(); const { unstakeRING, isPending, isLoadingOldAndNewPrev } = useUnstakeRING({ collator, inputAmount: parseEther(amount) }); const handleUnstake = useCallback(async () => { + const { isDeployed } = await checkWaitingIndexing(); + if (!isDeployed) { + return; + } const tx = await unstakeRING()?.catch((e) => { error(e.shortMessage); }); if (tx) { setHash(tx); } - }, [unstakeRING]); + }, [checkWaitingIndexing, unstakeRING]); const handleFail = useCallback(() => { setHash(undefined); @@ -84,7 +89,7 @@ const Unstake = ({ color="primary" className="w-full" isDisabled={amount === '0'} - isLoading={isPending || isLoadingOldAndNewPrev} + isLoading={isPending || isLoadingOldAndNewPrev || isLoadingWaitingIndexing} onClick={handleUnstake} > Unstake diff --git a/src/view/stake/_components/new/index.tsx b/src/view/stake/_components/new/index.tsx index c233ccb..059a42a 100644 --- a/src/view/stake/_components/new/index.tsx +++ b/src/view/stake/_components/new/index.tsx @@ -11,6 +11,7 @@ import { } from '@nextui-org/react'; import { ChevronDown, X } from 'lucide-react'; +import useWalletStatus from '@/hooks/useWalletStatus'; import { TransitionPanel } from '@/components/transition-panel'; import Avatar from '@/components/avatar'; import { stakeTabs } from '@/config/tabs'; @@ -29,7 +30,8 @@ interface NewStakeModalProps { isOpen?: boolean; } const NewStakeModal = ({ onClose, isOpen, onSuccess }: NewStakeModalProps) => { - const [selected, setSelected] = useState(stakeTabs[0].key); + const { currentChain } = useWalletStatus(); + const [selected, setSelected] = useState('stake-ring'); const [selectedCollator, setSelectedCollator] = useState(undefined); const [selectCollatorOpen, setSelectCollatorOpen] = useState(false); @@ -129,7 +131,7 @@ const NewStakeModal = ({ onClose, isOpen, onSuccess }: NewStakeModalProps) => { tabContent: 'group-data-[selected=true]:text-foreground font-bold' }} > - {stakeTabs.map((tab) => ( + {stakeTabs(currentChain?.nativeCurrency?.symbol || 'RING').map((tab) => ( { const [approvalHash, setApprovalHash] = useState<`0x${string}` | undefined>(undefined); const [checkedDeposits, setCheckedDeposits] = useState([]); const { ringDAOGovernance } = useWalletStatus(); - + const { checkWaitingIndexing, isLoading: isLoadingWaitingIndexing } = useCheckWaitingIndexing(); const { data: isApprovedForAll, isLoading: isLoadingIsApprovedForAll, @@ -38,6 +40,10 @@ const StakeDeposit = ({ selectedCollator, onSuccess }: StakeDepositProps) => { }); const handleStake = useCallback(async () => { + const { isDeployed } = await checkWaitingIndexing(); + if (!isDeployed) { + return; + } if (!isApprovedForAll) { const tx = await handleApprovalForAll()?.catch((e) => { error(e.shortMessage); @@ -53,7 +59,7 @@ const StakeDeposit = ({ selectedCollator, onSuccess }: StakeDepositProps) => { setHash(tx); } } - }, [handleApprovalForAll, isApprovedForAll, handleDepositStake]); + }, [checkWaitingIndexing, handleApprovalForAll, isApprovedForAll, handleDepositStake]); const handleApprovalTransactionSuccess = useCallback(async () => { refetchIsApprovedForAll(); @@ -128,7 +134,7 @@ const StakeDeposit = ({ selectedCollator, onSuccess }: StakeDepositProps) => {

- Please note that gRING is non-transferable. + Please note that {gringTokenInfo?.symbol} is non-transferable.