diff --git a/explorer/src/components/Consensus/Account/AccountLatestRewards.tsx b/explorer/src/components/Consensus/Account/AccountLatestRewards.tsx index 9f8aba9c..fd1f6877 100644 --- a/explorer/src/components/Consensus/Account/AccountLatestRewards.tsx +++ b/explorer/src/components/Consensus/Account/AccountLatestRewards.tsx @@ -1,9 +1,12 @@ +import { StatusIcon } from '@/components/common/StatusIcon' +import { Tooltip } from '@/components/common/Tooltip' import { INTERNAL_ROUTES } from 'constants/routes' import { AccountByIdQuery } from 'gql/graphql' import useIndexers from 'hooks/useIndexers' import Link from 'next/link' import { useParams, useRouter } from 'next/navigation' import { FC } from 'react' +import { useConsensusStates } from 'states/consensus' import { AccountIdParam } from 'types/app' import { bigNumberToNumber } from 'utils/number' @@ -15,6 +18,7 @@ interface AccountLatestRewardsProps { export const AccountLatestRewards: FC = ({ rewards }) => { const { network, section, tokenSymbol } = useIndexers() const { accountId } = useParams() + const lastBlockNumber = useConsensusStates((state) => state.lastBlockNumber) const { push } = useRouter() return ( @@ -33,42 +37,53 @@ export const AccountLatestRewards: FC = ({ rewards })
    - {rewards.map(({ id, rewardType, blockHeight, amount }, index) => ( -
  1. -
    -
    -
    - - {blockHeight} - + {rewards.map(({ id, rewardType, blockHeight, amount }, index) => { + const confirmations = lastBlockNumber ? Math.max(0, lastBlockNumber - blockHeight) : 0 + return ( +
  2. +
    +
    +
    + + {blockHeight} + + {`${confirmations} confirmations`} + } + direction='top' + > + = 10} isPending={confirmations < 10} /> + +
    -
-
- {rewardType - .split('.')[1] - .split(/(?=[A-Z])/) - .join(' ')} -
-
- {bigNumberToNumber(amount)} {tokenSymbol} -
- - ))} +
+ {rewardType + .split('.')[1] + .split(/(?=[A-Z])/) + .join(' ')} +
+
+ {bigNumberToNumber(amount)} {tokenSymbol} +
+ + ) + })} diff --git a/explorer/src/components/Consensus/Account/AccountRewardList.tsx b/explorer/src/components/Consensus/Account/AccountRewardList.tsx index caf97efd..b2194619 100644 --- a/explorer/src/components/Consensus/Account/AccountRewardList.tsx +++ b/explorer/src/components/Consensus/Account/AccountRewardList.tsx @@ -1,5 +1,6 @@ 'use client' +import { StatusIcon } from '@/components/common/StatusIcon' import { useApolloClient } from '@apollo/client' import { shortString } from '@autonomys/auto-utils' import { sendGAEvent } from '@next/third-parties/google' @@ -22,6 +23,7 @@ import Link from 'next/link' import { useParams } from 'next/navigation' import { FC, useCallback, useEffect, useMemo, useState } from 'react' import { useInView } from 'react-intersection-observer' +import { useConsensusStates } from 'states/consensus' import { hasValue, isLoading, useQueryStates } from 'states/query' import { AccountIdParam } from 'types/app' import type { Cell } from 'types/table' @@ -48,7 +50,7 @@ export const AccountRewardList: FC = () => { const isLargeLaptop = useMediaQuery('(min-width: 1440px)') const { accountId } = useParams() const inFocus = useWindowFocus() - + const lastBlockNumber = useConsensusStates((state) => state.lastBlockNumber) const orderBy = useMemo( () => sorting && sorting.length > 0 @@ -171,8 +173,29 @@ export const AccountRewardList: FC = () => { ), }, + { + accessorKey: 'block_height', + header: 'Confirmation', + enableSorting: true, + cell: ({ row }: Cell) => { + const confirmations = lastBlockNumber + ? Math.max(0, lastBlockNumber - row.original.block_height) + : 0 + return ( +
+ = 10} isPending={confirmations < 10} /> + + {confirmations} + +
+ ) + }, + }, ], - [network, section, isLargeLaptop, tokenSymbol], + [network, section, isLargeLaptop, tokenSymbol, lastBlockNumber], ) const pageCount = useMemo( diff --git a/explorer/src/components/common/OutOfSyncBanner.tsx b/explorer/src/components/common/OutOfSyncBanner.tsx index 0e876ae7..ee8a6e87 100644 --- a/explorer/src/components/common/OutOfSyncBanner.tsx +++ b/explorer/src/components/common/OutOfSyncBanner.tsx @@ -1,13 +1,16 @@ import { useQuery } from '@apollo/client' +import { blockNumber } from '@autonomys/auto-consensus' import { activate, NetworkId } from '@autonomys/auto-utils' import { EXTERNAL_ROUTES } from 'constants/routes' import { LastBlockQuery } from 'gql/graphql' import useIndexers from 'hooks/useIndexers' import Link from 'next/link' -import React, { FC, useCallback, useEffect, useMemo, useState } from 'react' +import React, { FC, useCallback, useEffect, useMemo } from 'react' +import { useConsensusStates } from 'states/consensus' import { LAST_BLOCK } from './query' const NORMAL_BLOCKS_DIVERGENCE = 120 +const POLL_INTERVAL = 12000 const OutOfSyncBanner: FC = () => { const { network } = useIndexers() @@ -47,19 +50,23 @@ const OutOfSyncBanner: FC = () => { export const useOutOfSyncBanner = () => { const { network } = useIndexers() - const [lastChainBlock, setLastChainBlock] = useState(null) + + const lastBlockNumber = useConsensusStates((state) => state.lastBlockNumber) + const setLastBlockNumber = useConsensusStates((state) => state.setLastBlockNumber) const { data } = useQuery(LAST_BLOCK, { - pollInterval: 30000, + pollInterval: POLL_INTERVAL, }) const getChainLastBlock = useCallback(async () => { - const api = await activate({ networkId: network }) - - const block = await api.rpc.chain.getBlock() - - setLastChainBlock(block.block.header.number.toNumber()) - }, [network]) + try { + const api = await activate({ networkId: network }) + setLastBlockNumber(await blockNumber(api)) + await api.disconnect() + } catch (error) { + console.error('Error getting chain last block', error) + } + }, [network, setLastBlockNumber]) const lastBlock = useMemo(() => data && parseInt(data.lastBlock[0].height), [data]) @@ -67,15 +74,20 @@ export const useOutOfSyncBanner = () => { () => data && lastBlock && - lastChainBlock !== null && - lastBlock + NORMAL_BLOCKS_DIVERGENCE < lastChainBlock ? ( + lastBlockNumber !== null && + lastBlock + NORMAL_BLOCKS_DIVERGENCE < lastBlockNumber ? ( ) : null, - [data, lastBlock, lastChainBlock], + [data, lastBlock, lastBlockNumber], ) useEffect(() => { getChainLastBlock() + const interval = setInterval(() => { + getChainLastBlock() + }, POLL_INTERVAL) + + return () => clearInterval(interval) }, [getChainLastBlock]) return outOfSyncBanner diff --git a/explorer/src/states/consensus.ts b/explorer/src/states/consensus.ts index 2e484aca..573e87fa 100644 --- a/explorer/src/states/consensus.ts +++ b/explorer/src/states/consensus.ts @@ -38,6 +38,9 @@ interface ConsensusDefaultState { successfulBundles: SuccessfulBundle[] deposits: Deposit[] withdrawals: Withdrawal[] + + // last block number + lastBlockNumber: number | null } interface ConsensusState extends ConsensusDefaultState { @@ -63,6 +66,7 @@ interface ConsensusState extends ConsensusDefaultState { setSuccessfulBundles: (successfulBundles: SuccessfulBundle[]) => void setDeposits: (deposits: Deposit[]) => void setWithdrawals: (withdrawals: Withdrawal[]) => void + setLastBlockNumber: (lastBlockNumber: number) => void clear: () => void } @@ -89,6 +93,9 @@ const initialState: ConsensusDefaultState = { successfulBundles: [], deposits: [], withdrawals: [], + + // last block number + lastBlockNumber: null, } export const useConsensusStates = create()( @@ -116,11 +123,12 @@ export const useConsensusStates = create()( setSuccessfulBundles: (successfulBundles) => set(() => ({ successfulBundles })), setDeposits: (deposits) => set(() => ({ deposits })), setWithdrawals: (withdrawals) => set(() => ({ withdrawals })), + setLastBlockNumber: (lastBlockNumber) => set(() => ({ lastBlockNumber })), clear: () => set(() => ({ ...initialState })), }), { name: 'consensus-storage', - version: 2, + version: 3, storage: createJSONStorage(() => localStorage), serialize: (state) => JSON.stringify(state, bigIntSerializer), deserialize: (str) => JSON.parse(str, bigIntDeserializer),