From 0d82655d6dcd3ef9e9b2a8013e72a8ce2feb8cfe Mon Sep 17 00:00:00 2001 From: WhiteMind Date: Wed, 25 Oct 2023 13:12:18 +0800 Subject: [PATCH 1/3] Refactor: some fetcher-related code (#121) * refactor: not using requesterV2 directly outside of ExplorerService * refactor: export the api in fetcher wrapped in an object * refactor: provide multiple utility functions for fetcher * refactor: use more appropriate utility functions for some fetch functions * refactor: TransactionCellScript * refactor: remove unnecessary types from fetcher * refactor: search component --- src/components/Card/HashCard/index.tsx | 6 +- .../Header/BlockchainComp/index.tsx | 7 +- .../NftCollectionInventory/index.tsx | 26 +- .../NftCollectionOverview/index.tsx | 17 +- .../NftCollectionTransfers/index.tsx | 12 +- src/components/NftItemTransfers/index.tsx | 33 +- src/components/Search/index.tsx | 97 +- src/components/Toast/index.tsx | 25 +- src/constants/common.ts | 8 - src/contexts/providers/dasQuery.tsx | 19 +- src/pages/Address/AddressComp.tsx | 38 +- src/pages/Address/index.tsx | 15 +- src/pages/BlockDetail/index.tsx | 18 +- .../FeeRateTracker/FeeRateTrackerComp.tsx | 1 + src/pages/FeeRateTracker/index.tsx | 28 +- src/pages/FeeRateTracker/types.d.ts | 32 - src/pages/Home/index.tsx | 5 +- src/pages/NervosDao/index.tsx | 26 +- src/pages/NftCollectionInfo/index.tsx | 145 +-- src/pages/NftCollections/List.tsx | 14 +- src/pages/NftCollections/index.tsx | 30 +- src/pages/NftInfo/index.tsx | 82 +- src/pages/Script/ScriptsComp.tsx | 34 +- src/pages/Script/index.tsx | 29 +- src/pages/Script/types.ts | 58 - src/pages/SimpleUDT/index.tsx | 39 +- .../activities/AddressBalanceRank.tsx | 7 +- .../activities/BalanceDistribution.tsx | 5 +- .../block/BlockTimeDistribution.tsx | 4 +- .../block/EpochTimeDistribution.tsx | 4 +- .../mining/MinerAddressDistribution.tsx | 4 +- .../monetary/AnnualPercentageCompensation.tsx | 4 +- .../monetary/InflationRate.tsx | 5 +- src/pages/Tokens/index.tsx | 14 +- .../TransactionCellScript/index.tsx | 312 +++-- .../TransactionCellScript/styled.tsx | 2 +- .../TransactionLite/TransactionLite.tsx | 4 +- src/pages/Transaction/index.tsx | 4 +- src/pages/TransactionList/index.tsx | 18 +- src/service/app/blockchain.ts | 23 +- src/service/app/charts/cache.ts | 21 +- src/services/ExplorerService/fetcher.ts | 1159 ++++++++++------- src/services/ExplorerService/index.ts | 11 +- src/services/ExplorerService/types.ts | 6 +- src/types/index.d.ts | 48 - src/utils/chart.ts | 1 + src/utils/hook.ts | 8 +- src/utils/transformer.ts | 4 +- src/utils/util.ts | 6 +- 49 files changed, 1145 insertions(+), 1373 deletions(-) delete mode 100644 src/pages/FeeRateTracker/types.d.ts delete mode 100644 src/pages/Script/types.ts diff --git a/src/components/Card/HashCard/index.tsx b/src/components/Card/HashCard/index.tsx index e2831e6f8..0059ffe33 100644 --- a/src/components/Card/HashCard/index.tsx +++ b/src/components/Card/HashCard/index.tsx @@ -72,12 +72,12 @@ export default ({ } const handleExportTxClick = async () => { - const res = await explorerService.api.requesterV2(`transactions/${hash}/raw`).catch(error => { + const raw = await explorerService.api.fetchTransactionRaw(hash).catch(error => { setToast({ message: error.message }) }) - if (!res) return + if (typeof raw !== 'object') return - const blob = new Blob([JSON.stringify(res.data, null, 2)]) + const blob = new Blob([JSON.stringify(raw, null, 2)]) const link = document.createElement('a') link.download = `tx-${hash}.json` diff --git a/src/components/Header/BlockchainComp/index.tsx b/src/components/Header/BlockchainComp/index.tsx index 274049728..b74653b38 100644 --- a/src/components/Header/BlockchainComp/index.tsx +++ b/src/components/Header/BlockchainComp/index.tsx @@ -122,10 +122,9 @@ export default memo(() => { const query = useQuery( ['node_version'], async () => { - const wrapper = await explorerService.api.fetchNodeVersion() - const nodeVersion = wrapper.attributes.version - storeCachedData(AppCachedKeys.Version, `${nodeVersion}&${new Date().getTime()}`) - return nodeVersion + const { version } = await explorerService.api.fetchNodeVersion() + storeCachedData(AppCachedKeys.Version, `${version}&${new Date().getTime()}`) + return version }, { keepPreviousData: true, diff --git a/src/components/NftCollectionInventory/index.tsx b/src/components/NftCollectionInventory/index.tsx index ab6747b02..b620fb240 100644 --- a/src/components/NftCollectionInventory/index.tsx +++ b/src/components/NftCollectionInventory/index.tsx @@ -1,4 +1,3 @@ -import type { AxiosResponse } from 'axios' import { Link } from 'react-router-dom' import { useQuery } from 'react-query' import { Base64 } from 'js-base64' @@ -10,31 +9,18 @@ import styles from './styles.module.scss' import { getPrimaryColor } from '../../constants/common' import { explorerService } from '../../services/ExplorerService' import { handleNftImgError, patchMibaoImg } from '../../utils/util' +import type { NFTItem } from '../../services/ExplorerService/fetcher' const primaryColor = getPrimaryColor() -type NftCollectionInventoryItem = { - icon_url: string | null - id: number - token_id: string - owner?: string - standard: string - cell: { - cell_index: number - data: string - status: string - tx_hash: string - } | null -} - const NftCollectionInventory: React.FC<{ - list: Array + list: Array collection: string isLoading: boolean }> = ({ list, collection, isLoading }) => { const { t } = useTranslation() - const { data: info } = useQuery>(['collection-info', collection], () => - explorerService.api.requesterV2(`nft/collections/${collection}`), + const { data: info } = useQuery(['collection-info', collection], () => + explorerService.api.fetchNFTCollection(collection), ) if (!list.length) { @@ -53,8 +39,8 @@ const NftCollectionInventory: React.FC<{ ) } - const renderCover = (item: NftCollectionInventoryItem) => { - const coverUrl = item.icon_url ?? info?.data.icon_url + const renderCover = (item: NFTItem) => { + const coverUrl = item.icon_url ?? info?.icon_url const cell = item?.cell const standard = item?.standard diff --git a/src/components/NftCollectionOverview/index.tsx b/src/components/NftCollectionOverview/index.tsx index ddc78cf95..9f7049c25 100644 --- a/src/components/NftCollectionOverview/index.tsx +++ b/src/components/NftCollectionOverview/index.tsx @@ -1,4 +1,3 @@ -import type { AxiosResponse } from 'axios' import { Link } from 'react-router-dom' import { useQuery } from 'react-query' import { Tooltip } from 'antd' @@ -10,23 +9,9 @@ import { getPrimaryColor } from '../../constants/common' const primaryColor = getPrimaryColor() -interface InfoRes { - id: number - standard: string - name: string - description: string - creator: string | null - icon_url: string | null - items_count: number | null - holders_count: number | null -} - const NftCollectionOverview = ({ id }: { id: string }) => { const { t } = useTranslation() - const { isLoading, data } = useQuery>(['collection-info', id], () => - explorerService.api.requesterV2(`nft/collections/${id}`), - ) - const info = data?.data + const { isLoading, data: info } = useQuery(['collection-info', id], () => explorerService.api.fetchNFTCollection(id)) return (
diff --git a/src/components/NftCollectionTransfers/index.tsx b/src/components/NftCollectionTransfers/index.tsx index 596a4810e..147942568 100644 --- a/src/components/NftCollectionTransfers/index.tsx +++ b/src/components/NftCollectionTransfers/index.tsx @@ -1,4 +1,3 @@ -import type { AxiosResponse } from 'axios' import { FC, useMemo, useState } from 'react' import { Link } from 'react-router-dom' import { useQuery } from 'react-query' @@ -7,7 +6,8 @@ import { Base64 } from 'js-base64' import { hexToBytes } from '@nervosnetwork/ckb-sdk-utils' import { useTranslation } from 'react-i18next' import { parseSporeCellData } from '../../utils/spore' -import type { TransferListRes, TransferRes } from '../../pages/NftCollectionInfo' +// TODO: Refactor is needed. Should not directly import anything from the descendants of ExplorerService. +import type { TransferListRes, TransferRes } from '../../services/ExplorerService/fetcher' import styles from './styles.module.scss' import { getPrimaryColor } from '../../constants/common' import { handleNftImgError, patchMibaoImg } from '../../utils/util' @@ -28,14 +28,14 @@ interface TransferCollectionProps { const NftCollectionTransfers: FC = props => { const { collection } = props - const { data: info } = useQuery>(['collection-info', collection], () => - explorerService.api.requesterV2(`nft/collections/${collection}`), + const { data: info } = useQuery(['collection-info', collection], () => + explorerService.api.fetchNFTCollection(collection), ) return (
- - + +
) } diff --git a/src/components/NftItemTransfers/index.tsx b/src/components/NftItemTransfers/index.tsx index b5aca7fde..6073d41ab 100644 --- a/src/components/NftItemTransfers/index.tsx +++ b/src/components/NftItemTransfers/index.tsx @@ -6,40 +6,11 @@ import { getPrimaryColor } from '../../constants/common' import { dayjs, useParseDate } from '../../utils/date' import styles from './styles.module.scss' import { useCurrentLanguage } from '../../utils/i18n' +import type { TransferRes } from '../../services/ExplorerService/fetcher' const primaryColor = getPrimaryColor() -export interface TransferListRes { - data: Array<{ - id: number - from: string | null - to: string | null - action: 'mint' | 'normal' | 'destruction' - item: { - id: number - collection_id: number - token_id: string - name: string | null - icon_url: string | null - owner_id: number - metadata_url: string | null - cell_id: number | null - } - transaction: { - tx_hash: string - block_number: number - block_timestamp: number - } - }> - pagination: { - count: number - page: number - next: number | null - prev: number | null - last: number - } -} -const NftItemTransfers: React.FC<{ list: TransferListRes['data']; isLoading: boolean }> = ({ list, isLoading }) => { +const NftItemTransfers: React.FC<{ list: TransferRes[]; isLoading: boolean }> = ({ list, isLoading }) => { const [isShowInAge, setIsShowInAge] = useState(false) const { t } = useTranslation() const parseDate = useParseDate() diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 9d9e4b090..3bb8c3540 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1,6 +1,5 @@ import { useState, useRef, useEffect, FC, memo, RefObject, ChangeEvent } from 'react' import { useHistory } from 'react-router' -import { AxiosError } from 'axios' import { TFunction, useTranslation } from 'react-i18next' import { SearchImage, SearchInputPanel, SearchPanel, SearchButton, SearchContainer } from './styled' import { explorerService, Response } from '../../services/ExplorerService' @@ -10,14 +9,9 @@ import { addPrefixForHash, containSpecialChar } from '../../utils/string' import { HttpErrorCode, SearchFailType } from '../../constants/common' import { useIsMobile } from '../../utils/hook' import { isChainTypeError } from '../../utils/chain' - -enum SearchResultType { - Block = 'block', - Transaction = 'ckb_transaction', - Address = 'address', - LockHash = 'lock_hash', - UDT = 'udt', -} +import { isAxiosError } from '../../utils/error' +// TODO: Refactor is needed. Should not directly import anything from the descendants of ExplorerService. +import { SearchResultType } from '../../services/ExplorerService/fetcher' const clearSearchInput = (inputElement: RefObject) => { const input = inputElement.current @@ -41,7 +35,7 @@ const setSearchContent = (inputElement: RefObject, content: st } } -const handleSearchResult = ( +const handleSearchResult = async ( searchValue: string, inputElement: RefObject, setSearchValue: Function, @@ -59,44 +53,53 @@ const handleSearchResult = ( } setSearchLoading(inputElement, t) - explorerService.api - .fetchSearchResult(addPrefixForHash(query)) - .then((response: any) => { - const { data } = response - if (!response || !data.type) { - history.push(`/search/fail?q=${query}`) - return - } - clearSearchInput(inputElement) - setSearchValue('') - if (data.type === SearchResultType.Block) { - history.push(`/block/${(data as Response.Wrapper).attributes.blockHash}`) - } else if (data.type === SearchResultType.Transaction) { - history.push(`/transaction/${(data as Response.Wrapper).attributes.transactionHash}`) - } else if (data.type === SearchResultType.Address) { - history.push(`/address/${(data as Response.Wrapper).attributes.addressHash}`) - } else if (data.type === SearchResultType.LockHash) { - history.push(`/address/${(data as Response.Wrapper).attributes.lockHash}`) - } else if (data.type === SearchResultType.UDT) { + + try { + const { data } = await explorerService.api.fetchSearchResult(addPrefixForHash(query)) + clearSearchInput(inputElement) + setSearchValue('') + + switch (data.type) { + case SearchResultType.Block: + history.push(`/block/${data.attributes.blockHash}`) + break + + case SearchResultType.Transaction: + history.push(`/transaction/${data.attributes.transactionHash}`) + break + + case SearchResultType.Address: + history.push(`/address/${data.attributes.addressHash}`) + break + + case SearchResultType.LockHash: + history.push(`/address/${data.attributes.lockHash}`) + break + + case SearchResultType.UDT: history.push(`/sudt/${query}`) - } - }) - .catch((error: AxiosError) => { - setSearchContent(inputElement, query) - if ( - error.response && - error.response.data && - error.response.status === 404 && - (error.response.data as Response.Error[]).find( - (errorData: Response.Error) => errorData.code === HttpErrorCode.NOT_FOUND_ADDRESS, - ) - ) { - clearSearchInput(inputElement) - history.push(`/address/${query}`) - } else { - history.push(`/search/fail?q=${query}`) - } - }) + break + + default: + break + } + } catch (error) { + setSearchContent(inputElement, query) + + if ( + isAxiosError(error) && + error.response?.data && + error.response.status === 404 && + (error.response.data as Response.Error[]).find( + (errorData: Response.Error) => errorData.code === HttpErrorCode.NOT_FOUND_ADDRESS, + ) + ) { + clearSearchInput(inputElement) + history.push(`/address/${query}`) + } else { + history.push(`/search/fail?q=${query}`) + } + } } const Search: FC<{ diff --git a/src/components/Toast/index.tsx b/src/components/Toast/index.tsx index c958f54f4..1df0011c8 100644 --- a/src/components/Toast/index.tsx +++ b/src/components/Toast/index.tsx @@ -3,7 +3,14 @@ import { useTimeoutWithUnmount } from '../../utils/hook' import { ToastItemPanel, ToastPanel } from './styled' import { createGlobalState, useGlobalState } from '../../utils/state' -const getColor = (type: 'success' | 'warning' | 'danger') => { +interface ToastMessage { + message: string + type: 'success' | 'warning' | 'danger' + duration?: number + id: number +} + +const getColor = (type: ToastMessage['type']) => { switch (type) { case 'success': return '#3cc68a' @@ -20,7 +27,7 @@ const ANIMATION_DISAPPEAR_TIME = 2000 const MAX_FRAME: number = (ANIMATION_DISAPPEAR_TIME / 1000) * 40 // suppose fps = 40 const DEFAULT_TOAST_DURATION = 3000 -const ToastItem = ({ data, willLeave }: { data: State.ToastMessage; willLeave: Function }) => { +const ToastItem = ({ data, willLeave }: { data: ToastMessage; willLeave: Function }) => { const [opacity, setOpacity] = useState(1) let animationId: number = 0 useTimeoutWithUnmount( @@ -60,19 +67,19 @@ const ToastItem = ({ data, willLeave }: { data: State.ToastMessage; willLeave: F } interface State { - toasts: State.ToastMessage[] + toasts: ToastMessage[] toast: string } interface Action { type: 'ADD' | 'REMOVE' payload: { - toast: State.ToastMessage + toast: ToastMessage } } const initialState: State = { - toasts: [] as State.ToastMessage[], + toasts: [], toast: '', } @@ -86,20 +93,20 @@ const reducer = (state: State, action: Action) => { case 'REMOVE': return { ...state, - toasts: state.toasts.filter((toast: State.ToastMessage) => toast.id !== action.payload.toast.id), + toasts: state.toasts.filter((toast: ToastMessage) => toast.id !== action.payload.toast.id), } default: return state } } -const globalToast = createGlobalState(null) +const globalToast = createGlobalState(null) export function useSetToast() { const [, setToast] = useGlobalState(globalToast) return useCallback( - (data: Pick & Partial>) => + (data: Pick & Partial>) => setToast({ id: new Date().getTime(), message: data.message, @@ -128,7 +135,7 @@ export default () => { return state.toasts.length === 0 ? null : ( {state.toasts && - state.toasts.map((item: State.ToastMessage) => ( + state.toasts.map((item: ToastMessage) => ( { dispatch({ diff --git a/src/constants/common.ts b/src/constants/common.ts index c6bc2792b..97d14b299 100644 --- a/src/constants/common.ts +++ b/src/constants/common.ts @@ -64,14 +64,6 @@ export const SearchFailType = { CHAIN_ERROR: 'chain_error', } -export enum CellState { - NONE = 0, - LOCK = 1, - TYPE = 2, - DATA = 3, - CAPACITY = 4, -} - export enum CellType { Input = 'input', Output = 'output', diff --git a/src/contexts/providers/dasQuery.tsx b/src/contexts/providers/dasQuery.tsx index f420225a2..3d82f6f46 100644 --- a/src/contexts/providers/dasQuery.tsx +++ b/src/contexts/providers/dasQuery.tsx @@ -1,14 +1,11 @@ import { createContext, FC, useCallback, useContext, useMemo, useRef } from 'react' import { useQuery } from 'react-query' import { explorerService } from '../../services/ExplorerService' +import type { DASAccount, DASAccountMap } from '../../services/ExplorerService/fetcher' import { unique } from '../../utils/array' import { throttle } from '../../utils/function' import { pick } from '../../utils/object' -export type DASAccount = string - -export type DASAccountMap = Record - export interface DASQueryContextValue { getDASAccounts: (addresses: string[]) => Promise } @@ -25,18 +22,6 @@ interface PendingQuery { } } -async function fetchDASAccounts(addresses: string[]): Promise { - const { data } = await explorerService.api.requesterV2.post>('das_accounts', { - addresses, - }) - const dataWithNormalizeEmptyValue = Object.fromEntries( - Object.entries(data).map(([addr, account]) => { - return account === '' ? [addr, null] : [addr, account] - }), - ) - return dataWithNormalizeEmptyValue -} - export const DASQueryContextProvider: FC = ({ children }) => { const accountMap = useRef({}) const pendingQueries = useRef([]) @@ -51,7 +36,7 @@ export const DASQueryContextProvider: FC = ({ children }) => { ) try { - const newAccountMap = await fetchDASAccounts(addressesWithMissCache) + const newAccountMap = await explorerService.api.fetchDASAccounts(addressesWithMissCache) Object.assign(accountMap.current, newAccountMap) queries.forEach(({ addresses, handler }) => { handler.resolve(pick(accountMap.current, addresses)) diff --git a/src/pages/Address/AddressComp.tsx b/src/pages/Address/AddressComp.tsx index 4cdf29964..747238fb8 100644 --- a/src/pages/Address/AddressComp.tsx +++ b/src/pages/Address/AddressComp.tsx @@ -26,6 +26,7 @@ import { ReactComponent as TimeDownIcon } from '../../assets/time_down.svg' import { ReactComponent as TimeUpIcon } from '../../assets/time_up.svg' import { sliceNftName } from '../../utils/string' import { + OrderByType, useIsLGScreen, useIsMobile, useNewAddr, @@ -188,23 +189,6 @@ const AddressUDTItem = ({ udtAccount }: { udtAccount: State.UDTAccount }) => { ) } -interface CoTAList { - data: Array<{ - id: number - token_id: number - owner: string - collection: { - id: number - name: string - description: string - icon_url: string - } - }> - pagination: { - series: Array - } -} - const lockScriptIcon = (show: boolean) => { if (show) { return isMainnet() ? ArrowUpIcon : ArrowUpBlueIcon @@ -275,22 +259,20 @@ export const AddressOverview: FC<{ address: State.Address }> = ({ address }) => const { t } = useTranslation() const { udtAccounts = [] } = address - const { data: initList } = useQuery>( + const { data: initList } = useQuery( ['cota-list', address.addressHash], - () => explorerService.api.requesterV2(`nft/items?owner=${address.addressHash}&standard=cota`), + () => explorerService.api.fetchNFTItemByOwner(address.addressHash, 'cota'), { enabled: !!address?.addressHash, }, ) - const { data: cotaList } = useQuery(['cota-list', initList?.data.pagination.series], () => + const { data: cotaList } = useQuery(['cota-list', initList?.pagination.series], () => Promise.all( - (initList?.data.pagination.series ?? []).map(p => - explorerService.api.requesterV2(`nft/items?owner=${address.addressHash}&standard=cota&page=${p}`), + (initList?.pagination.series ?? []).map(p => + explorerService.api.fetchNFTItemByOwner(address.addressHash, 'cota', p), ), - ).then(list => { - return list.reduce((total, acc) => [...total, ...acc.data.data], [] as CoTAList['data']) - }), + ).then(resList => resList.flatMap(res => res.data)), ) return ( @@ -308,11 +290,11 @@ export const AddressOverview: FC<{ address: State.Address }> = ({ address }) => symbol: cota.collection.name, amount: '', typeHash: '', - udtIconFile: cota.collection.icon_url, + udtIconFile: cota.collection.icon_url ?? '', udtType: 'cota', cota: { cotaId: cota.collection.id, - tokenId: cota.token_id, + tokenId: Number(cota.token_id), }, uan: undefined, collection: undefined, @@ -337,7 +319,7 @@ export const AddressTransactions = ({ address: string transactions: State.Transaction[] transactionsTotal: number - timeOrderBy: State.SortOrderTypes + timeOrderBy: OrderByType }) => { const isMobile = useIsMobile() const { t } = useTranslation() diff --git a/src/pages/Address/index.tsx b/src/pages/Address/index.tsx index 6b7953907..3a96253f3 100644 --- a/src/pages/Address/index.tsx +++ b/src/pages/Address/index.tsx @@ -18,28 +18,21 @@ export const Address = () => { // REFACTOR: avoid using useSortParam const { sortBy, orderBy, sort } = useSortParam<'time'>(s => s === 'time') - const addressInfoQuery = useQuery(['address_info', address], async () => { - const wrapper = await explorerService.api.fetchAddressInfo(address) - const result: State.Address = { - ...wrapper.attributes, - type: wrapper.type === 'lock_hash' ? 'LockHash' : 'Address', - } - return result - }) + const addressInfoQuery = useQuery(['address_info', address], () => explorerService.api.fetchAddressInfo(address)) const addressTransactionsQuery = useQuery( ['address_transactions', address, currentPage, pageSize, sort], async () => { try { - const { data, meta } = await explorerService.api.fetchTransactionsByAddress( + const { data: transactions, total } = await explorerService.api.fetchTransactionsByAddress( address, currentPage, pageSize, sort, ) return { - transactions: data.map(wrapper => wrapper.attributes), - total: meta ? meta.total : 0, + transactions, + total, } } catch (err) { const isEmptyAddress = isAxiosError(err) && err.response?.status === 404 diff --git a/src/pages/BlockDetail/index.tsx b/src/pages/BlockDetail/index.tsx index 1e681e8eb..21bdda1f3 100644 --- a/src/pages/BlockDetail/index.tsx +++ b/src/pages/BlockDetail/index.tsx @@ -19,11 +19,7 @@ export default () => { const filter = new URLSearchParams(search).get('filter') - const queryBlock = useQuery(['block', blockHeightOrHash], async () => { - const wrapper = await explorerService.api.fetchBlock(blockHeightOrHash) - const block = wrapper.attributes - return block - }) + const queryBlock = useQuery(['block', blockHeightOrHash], () => explorerService.api.fetchBlock(blockHeightOrHash)) const blockHash = queryBlock.data?.blockHash const block = queryBlock.data ?? defaultBlockInfo @@ -32,16 +28,16 @@ export default () => { async () => { assert(blockHash != null) try { - const { data, meta } = await explorerService.api.fetchTransactionsByBlockHash(blockHash, { + const { + data: transactions, + total, + pageSize, + } = await explorerService.api.fetchTransactionsByBlockHash(blockHash, { page: currentPage, size: pageSizeParam, filter, }) - return { - transactions: data.map(wrapper => wrapper.attributes), - total: meta?.total ?? 0, - pageSize: meta?.pageSize, - } + return { transactions, total, pageSize } } catch (e) { console.error(e) return { diff --git a/src/pages/FeeRateTracker/FeeRateTrackerComp.tsx b/src/pages/FeeRateTracker/FeeRateTrackerComp.tsx index dc501e10d..df348d11b 100644 --- a/src/pages/FeeRateTracker/FeeRateTrackerComp.tsx +++ b/src/pages/FeeRateTracker/FeeRateTrackerComp.tsx @@ -10,6 +10,7 @@ import { ReactComponent as CarIcon } from '../../assets/car.svg' import { ReactComponent as RocketIcon } from '../../assets/rocket.svg' import { ChartColor } from '../../constants/common' import { useCurrentLanguage } from '../../utils/i18n' +import type { FeeRateTracker } from '../../services/ExplorerService/fetcher' const textStyleInChart: echarts.EChartOption.TextStyle = { color: '#999999', diff --git a/src/pages/FeeRateTracker/index.tsx b/src/pages/FeeRateTracker/index.tsx index 1f36ba05e..2227526e2 100644 --- a/src/pages/FeeRateTracker/index.tsx +++ b/src/pages/FeeRateTracker/index.tsx @@ -3,7 +3,6 @@ import { useQuery } from 'react-query' import { useTranslation } from 'react-i18next' import styles from './styles.module.scss' import Content from '../../components/Content' -import { toCamelcase } from '../../utils/util' import { useAnimationFrame, useIsMobile } from '../../utils/hook' import { ConfirmationTimeFeeRateChart, @@ -15,8 +14,9 @@ import Loading from '../../components/Loading' import { localeNumberString } from '../../utils/number' import { getFeeRateSamples } from '../../utils/chart' import { explorerService, useStatistics } from '../../services/ExplorerService' +import { FeeRateTracker } from '../../services/ExplorerService/fetcher' -const FeeRateTracker = () => { +const FeeRateTrackerPage = () => { const { t } = useTranslation() const lastFetchedTime = useRef(Number.MAX_SAFE_INTEGER) const deltaSecond = useRef(0) @@ -27,20 +27,14 @@ const FeeRateTracker = () => { const { data: transactionFeesStatistic } = useQuery( ['statistics-transaction_fees'], - () => - explorerService.api.requesterV2.get(`statistics/transaction_fees`).then(({ status, data }) => { - if (status === 200 && data) { - lastFetchedTime.current = Date.now() - deltaSecond.current = 0 - setSecondAfterUpdate(0) - return toCamelcase(data) - } - return { - transactionFeeRates: [], - pendingTransactionFeeRates: [], - lastNDaysTransactionFeeRates: [], - } - }), + async () => { + const res = await explorerService.api.fetchStatisticTransactionFees() + lastFetchedTime.current = Date.now() + deltaSecond.current = 0 + // TODO: refactor to dataUpdatedAt? + setSecondAfterUpdate(0) + return res + }, { refetchInterval: 1000 * 60, }, @@ -124,4 +118,4 @@ const FeeRateTracker = () => { ) } -export default FeeRateTracker +export default FeeRateTrackerPage diff --git a/src/pages/FeeRateTracker/types.d.ts b/src/pages/FeeRateTracker/types.d.ts deleted file mode 100644 index f80c06370..000000000 --- a/src/pages/FeeRateTracker/types.d.ts +++ /dev/null @@ -1,32 +0,0 @@ -declare namespace FeeRateTracker { - export interface TransactionFeeRate { - id: number - timestamp: number - feeRate: number - confirmationTime: number - } - - export interface PendingTransactionFeeRate { - id: number - feeRate: number - } - - export interface LastNDaysTransactionFeeRate { - date: string - feeRate: string - } - - export interface TransactionFeesStatistic { - transactionFeeRates: TransactionFeeRate[] - pendingTransactionFeeRates: PendingTransactionFeeRate[] - lastNDaysTransactionFeeRates: LastNDaysTransactionFeeRate[] - } - - export interface FeeRateCard { - priority: string - icon: ReactComponent - feeRate?: string - priorityClass: string - confirmationTime: number - } -} diff --git a/src/pages/Home/index.tsx b/src/pages/Home/index.tsx index 8cf9aa046..eb773c3cd 100644 --- a/src/pages/Home/index.tsx +++ b/src/pages/Home/index.tsx @@ -212,11 +212,10 @@ export default () => { const transactionsQuery = useQuery( ['latest_transactions'], async () => { - const { data, meta } = await explorerService.api.fetchLatestTransactions(ListPageParams.PageSize) - const transactions = data.map(wrapper => wrapper.attributes) + const { data: transactions, total } = await explorerService.api.fetchLatestTransactions(ListPageParams.PageSize) return { transactions, - total: meta?.total ?? transactions.length, + total, } }, { diff --git a/src/pages/NervosDao/index.tsx b/src/pages/NervosDao/index.tsx index 309d5c2a5..65a59ef9b 100644 --- a/src/pages/NervosDao/index.tsx +++ b/src/pages/NervosDao/index.tsx @@ -21,37 +21,29 @@ export const NervosDao = () => { const params = useSearchParams('tab', 'filter') const tab = (params.tab as 'transactions' | 'depositors') || 'transactions' - const queryNervosDao = useQuery(['nervos-dao'], async () => { - const wrapper = await explorerService.api.fetchNervosDao() - const nervosDao = wrapper.attributes - return nervosDao - }) + const queryNervosDao = useQuery(['nervos-dao'], () => explorerService.api.fetchNervosDao()) const queryNervosDaoTransactions = useQuery( ['nervos-dao-transactions', currentPage, _pageSize, params.filter], async () => { - const { data, meta } = await explorerService.api.fetchNervosDaoTransactionsByFilter({ + const { + data: transactions, + total, + pageSize, + } = await explorerService.api.fetchNervosDaoTransactionsByFilter({ filter: params.filter, page: currentPage, size: _pageSize, }) - const transactions = data.map(wrapper => wrapper.attributes) - return { - transactions, - total: meta?.total ?? transactions.length, - pageSize: meta?.pageSize, - } + return { transactions, total, pageSize } }, { enabled: !params.tab || params.tab === 'transactions' }, ) const queryNervosDaoDepositors = useQuery( ['nervos-dao-depositors'], - async () => { - const { data } = await explorerService.api.fetchNervosDaoDepositors() - return { depositors: data.map(wrapper => wrapper.attributes) } - }, + () => explorerService.api.fetchNervosDaoDepositors(), { enabled: params.tab === 'depositors', }, @@ -107,7 +99,7 @@ export const NervosDao = () => { ) : ( - {data => } + {data => } )} diff --git a/src/pages/NftCollectionInfo/index.tsx b/src/pages/NftCollectionInfo/index.tsx index 05837b63b..4decc3f1d 100644 --- a/src/pages/NftCollectionInfo/index.tsx +++ b/src/pages/NftCollectionInfo/index.tsx @@ -1,4 +1,3 @@ -import type { AxiosResponse } from 'axios' import { Link, useParams, useHistory } from 'react-router-dom' import { useQuery } from 'react-query' import { Popover } from 'antd' @@ -18,75 +17,6 @@ import PaginationWithRear from '../../components/PaginationWithRear' import NftHolderList from '../../components/NftHolderList' import Pagination from '../../components/Pagination' -export interface InventoryRes { - data: Array<{ - id: number - collection_id: number - icon_url: string | null - owner_id: number - token_id: string - standard: string - cell: { - cell_index: number - data: string - status: string - tx_hash: string - } | null - }> - pagination: { - count: number - page: number - next: number | null - prev: number | null - last: number - } -} - -export interface TransferRes { - id: number - from: string | null - to: string | null - action: 'mint' | 'transfer' - item: { - id: number - token_id: string - name: string | null - icon_url: string | null - owner_id: number - metadata_url: string | null - cell_id: number | null - standard: string | null - cell: { - cell_index: number - data: string - status: string - tx_hash: string - } | null - type_script: { - script_hash: string - } - } - transaction: { - tx_hash: string - block_number: number - block_timestamp: number - } -} - -export interface TransferListRes { - data: Array - pagination: { - count: number - page: number - next: number | null - prev: number | null - last: number - } -} -export interface HolderListRes { - data: Record -} - const tabs = ['transfers', 'holders', 'inventory'] function getFilterList(t: TFunction): Array> { return [ @@ -117,45 +47,32 @@ const NftCollectionInfo = () => { const filteredList = getFilterList(t) const isFilteredByType = filteredList.some(f => f.value === type) - const { isLoading: isTransferListLoading, data: transferListRes } = useQuery>( + const { isLoading: isTransferListLoading, data: transferListRes } = useQuery( ['nft-collection-transfer-list', id, page, filter, type], () => - explorerService.api.requesterV2(`/nft/transfers`, { - params: { - page, - collection_id: id, - transfer_action: isFilteredByType ? type : null, - address_hash: filter?.startsWith('0x') ? null : filter, - tx_hash: filter?.startsWith('0x') ? filter : null, - }, - }), + explorerService.api.fetchNFTCollectionTransferList( + id, + page, + null, + isFilteredByType ? type : null, + filter?.startsWith('0x') ? null : filter, + filter?.startsWith('0x') ? filter : null, + ), { enabled: tab === tabs[0], }, ) - const { isLoading: isHolderListLoading, data: rawHolderList } = useQuery>( + const { isLoading: isHoldersLoading, data: rawHolders } = useQuery( ['nft-collection-holder-list', id, page, sort, filter], - () => - explorerService.api.requesterV2(`/nft/collections/${id}/holders`, { - params: { - page, - sort, - address_hash: filter || null, - }, - }), + () => explorerService.api.fetchNFTCollectionHolders(id, page, sort, filter || null), { enabled: tab === tabs[1], }, ) - const { isLoading: isInventoryLoading, data: inventoryList } = useQuery>( + const { isLoading: isInventoryLoading, data: inventoryList } = useQuery( ['nft-collection-inventory', id, page], - () => - explorerService.api.requesterV2(`/nft/collections/${id}/items`, { - params: { - page, - }, - }), + () => explorerService.api.fetchNFTCollectionItems(id, page), { enabled: tab === tabs[2], }, @@ -169,11 +86,11 @@ const NftCollectionInfo = () => { history.push(`/nft-collections/${id}?${new URLSearchParams({ ...query, page: pageNo.toString() }).toString()}`) } - const holderList = rawHolderList - ? Object.keys(rawHolderList.data.data) + const holderList = rawHolders + ? Object.keys(rawHolders.data) .map(addr => ({ addr, - quantity: rawHolderList.data.data[addr], + quantity: rawHolders.data[addr], })) .sort((h1, h2) => { if (sort === 'quantity.asc') { @@ -187,16 +104,12 @@ const NftCollectionInfo = () => { let pageSource: | { - data: { - pagination: Record<'page' | 'last', number> - } + pagination: Record<'page' | 'last', number> } | undefined = { - data: { - pagination: { - page: 1, - last: 1, - }, + pagination: { + page: 1, + last: 1, }, } @@ -207,8 +120,8 @@ const NftCollectionInfo = () => { } const pages = { - currentPage: pageSource?.data.pagination.page ?? 1, - totalPages: pageSource?.data.pagination.last ?? 1, + currentPage: pageSource?.pagination.page ?? 1, + totalPages: pageSource?.pagination.last ?? 1, } return ( @@ -272,13 +185,13 @@ const NftCollectionInfo = () => { {tab === tabs[0] ? ( <> } /> @@ -288,7 +201,7 @@ const NftCollectionInfo = () => { <> { ) : null} {tab === tabs[2] ? ( <> - + ) : null} diff --git a/src/pages/NftCollections/List.tsx b/src/pages/NftCollections/List.tsx index 1ed1df7bf..ad80f999d 100644 --- a/src/pages/NftCollections/List.tsx +++ b/src/pages/NftCollections/List.tsx @@ -10,6 +10,7 @@ import { ReactComponent as FilterIcon } from '../../assets/filter_icon.svg' import { getPrimaryColor } from '../../constants/common' import { useIsMobile, useSearchParams, useSortParam } from '../../utils/hook' import styles from './styles.module.scss' +import type { NFTCollection } from '../../services/ExplorerService/fetcher' type NftSortField = 'transactions' | 'holder' | 'minted' const primaryColor = getPrimaryColor() @@ -43,19 +44,6 @@ export const isTxFilterType = (s?: string): boolean => { return s ? ['all', 'm_nft', 'nrc721', 'cota', 'spore'].includes(s) : false } -export interface NFTCollection { - id: number - standard: string - name: string - description: string - h24_ckb_transactions_count: string - creator: string | null - icon_url: string | null - items_count: number | null - holders_count: number | null - type_script: { code_hash: string; hash_type: 'data' | 'type'; args: string } | null -} - const TypeFilter = () => { const isMobile = useIsMobile() const { t } = useTranslation() diff --git a/src/pages/NftCollections/index.tsx b/src/pages/NftCollections/index.tsx index f4dbbacf8..74b4d84b8 100644 --- a/src/pages/NftCollections/index.tsx +++ b/src/pages/NftCollections/index.tsx @@ -1,9 +1,8 @@ -import type { AxiosResponse } from 'axios' import { useHistory, useLocation } from 'react-router-dom' import { useQuery } from 'react-query' import { useTranslation } from 'react-i18next' import Content from '../../components/Content' -import { NFTCollection, ListOnDesktop, ListOnMobile, isTxFilterType } from './List' +import { ListOnDesktop, ListOnMobile, isTxFilterType } from './List' import Pagination from '../../components/Pagination' import { getPrimaryColor } from '../../constants/common' import { explorerService } from '../../services/ExplorerService' @@ -15,17 +14,6 @@ const primaryColor = getPrimaryColor() type NftSortByType = 'transactions' | 'holder' | 'minted' -interface Res { - data: Array - pagination: { - count: number - page: number - next: number | null - prev: number | null - last: number - } -} - const submitTokenInfoUrl = udtSubmitEmail() const NftCollections = () => { @@ -38,17 +26,11 @@ const NftCollections = () => { const isValidFilter = isTxFilterType(type) && type !== 'all' - const { isLoading, data } = useQuery>(['nft-collections', page, sort, type], () => - explorerService.api.requesterV2('nft/collections', { - params: { - page, - sort, - type: isValidFilter ? type : undefined, - }, - }), + const { isLoading, data } = useQuery(['nft-collections', page, sort, type], () => + explorerService.api.fetchNFTCollections(page, sort, isValidFilter ? type : undefined), ) - const list = data?.data.data ?? [] + const list = data?.data ?? [] const handlePageChange = (pageNo: number) => { if (pageNo === +page) { @@ -80,8 +62,8 @@ const NftCollections = () => {
diff --git a/src/pages/NftInfo/index.tsx b/src/pages/NftInfo/index.tsx index 5553a4a35..e720079fa 100644 --- a/src/pages/NftInfo/index.tsx +++ b/src/pages/NftInfo/index.tsx @@ -1,11 +1,10 @@ -import type { AxiosResponse } from 'axios' import { Link, useParams, useHistory } from 'react-router-dom' import { useQuery } from 'react-query' import { Tooltip } from 'antd' import { Base64 } from 'js-base64' import { hexToBytes } from '@nervosnetwork/ckb-sdk-utils' import { useTranslation } from 'react-i18next' -import NftItemTransfers, { TransferListRes } from '../../components/NftItemTransfers' +import NftItemTransfers from '../../components/NftItemTransfers' import Pagination from '../../components/Pagination' import { ReactComponent as Cover } from '../../assets/nft_cover.svg' import { explorerService } from '../../services/ExplorerService' @@ -22,44 +21,13 @@ const NftInfo = () => { const history = useHistory() const { t } = useTranslation() const { page = '1' } = useSearchParams('page') - const { data } = useQuery< - AxiosResponse<{ - id: number - collection_id: number - token_id: string - name: string | null - icon_url: string | null - owner: string | null - metadata_url: string | null - standard: string | null - cell: { - status: string - tx_hash: string - cell_index: number - data: string | null - } | null - collection: { - id: number - standard: string - name: string - creator: string - icon_url: string - } - }> - >(['nft-item-info', collection, id], () => - explorerService.api.requesterV2(`nft/collections/${collection}/items/${id}`), + const { data } = useQuery(['nft-item-info', collection, id], () => + explorerService.api.fetchNFTCollectionItem(collection, id), ) - const { isLoading: isTransferListLoading, data: transferListRes } = useQuery>( + const { isLoading: isTransferListLoading, data: transferListRes } = useQuery( ['nft-item-transfer-list', collection, id, page], - () => - explorerService.api.requesterV2(`/nft/transfers`, { - params: { - page, - collection_id: collection, - token_id: id, - }, - }), + () => explorerService.api.fetchNFTCollectionTransferList(collection, page, id), ) const handlePageChange = (pageNo: number) => { @@ -68,11 +36,11 @@ const NftInfo = () => { } history.push(`/nft-info/${collection}/${id}?page=${pageNo}`) } - const coverUrl = data?.data.icon_url || data?.data.collection.icon_url + const coverUrl = data?.icon_url || data?.collection.icon_url const renderCover = () => { - const cell = data?.data.cell - const standard = data?.data.standard + const cell = data?.cell + const standard = data?.standard if (standard === 'spore' && cell && cell.data) { const sporeData = parseSporeCellData(cell.data) @@ -93,7 +61,7 @@ const NftInfo = () => { if (coverUrl) { return ( cover {
{renderCover()}
-
{data ? `${data.data.collection.name} #${data.data.token_id}` : '-'}
+
{data ? `${data.collection.name} #${data.token_id}` : '-'}
{t('nft.owner')}
- {data?.data.owner ? ( + {data?.owner ? ( - - {`${data.data.owner.slice(0, 12)}...${data.data.owner.slice( - -12, - )}`} + + {`${data.owner.slice(0, 12)}...${data.owner.slice(-12)}`} ) : ( @@ -137,19 +103,19 @@ const NftInfo = () => {
{t('nft.minter_address')}
- {data?.data.collection.creator ? ( + {data?.collection.creator ? ( - - {`${data.data.collection.creator.slice( + + {`${data.collection.creator.slice( 0, 12, - )}...${data.data.collection.creator.slice(-12)}`} + )}...${data.collection.creator.slice(-12)}`} ) : ( @@ -159,21 +125,21 @@ const NftInfo = () => {
Token ID
-
{`#${data?.data.token_id}`}
+
{`#${data?.token_id}`}
{t('nft.standard')}
-
{data ? t(`nft.${data.data.collection.standard}`) : '-'}
+
{data ? t(`nft.${data.collection.standard}`) : '-'}
{t(`nft.activity`)}
- +
diff --git a/src/pages/Script/ScriptsComp.tsx b/src/pages/Script/ScriptsComp.tsx index dc6d4a43f..676e2f6de 100644 --- a/src/pages/Script/ScriptsComp.tsx +++ b/src/pages/Script/ScriptsComp.tsx @@ -1,21 +1,19 @@ import { useState } from 'react' import { useHistory } from 'react-router' import { useQuery } from 'react-query' -import { AxiosResponse } from 'axios' import camelcase from 'camelcase' import { useParams } from 'react-router-dom' import { useTranslation } from 'react-i18next' import Pagination from '../../components/Pagination' import TransactionItem from '../../components/TransactionItem/index' -import { explorerService, Response } from '../../services/ExplorerService' +import { explorerService } from '../../services/ExplorerService' import { TransactionCellDetailModal, TransactionCellInfoPanel } from '../Transaction/TransactionCell/styled' import SimpleButton from '../../components/SimpleButton' import SimpleModal from '../../components/Modal' import TransactionCellScript from '../Transaction/TransactionCellScript' -import { shannonToCkb, toCamelcase } from '../../utils/util' +import { shannonToCkb } from '../../utils/util' import { localeNumberString } from '../../utils/number' import DecimalCapacity from '../../components/DecimalCapacity' -import { CellInScript, CkbTransactionInScript, PageInfo } from './types' import styles from './styles.module.scss' import { QueryResult } from '../../components/QueryResult' import AddressText from '../../components/AddressText' @@ -29,18 +27,7 @@ export const ScriptTransactions = ({ page, size }: { page: number; size: number const { codeHash, hashType } = useParams<{ codeHash: string; hashType: string }>() const transactionsQuery = useQuery(['scripts_ckb_transactions', codeHash, hashType, page, size], async () => { - const { data } = await explorerService.api.requesterV2 - .get(`scripts/ckb_transactions`, { - params: { - code_hash: codeHash, - hash_type: hashType, - page, - page_size: size, - }, - }) - .then((res: AxiosResponse) => - toCamelcase>(res.data), - ) + const { data } = await explorerService.api.fetchScriptCKBTransactions(codeHash, hashType, page, size) if (data == null || data.ckbTransactions == null || data.ckbTransactions.length === 0) { throw new Error('Transactions empty') @@ -120,20 +107,7 @@ export const ScriptCells = ({ const { codeHash, hashType } = useParams<{ codeHash: string; hashType: string }>() const cellsQuery = useQuery([`scripts_${cellType}`, codeHash, hashType, page, size], async () => { - const { data } = await explorerService.api.requesterV2 - .get(`scripts/${cellType}`, { - params: { - code_hash: codeHash, - hash_type: hashType, - page, - page_size: size, - }, - }) - .then((res: AxiosResponse) => - toCamelcase< - Response.Response<{ deployedCells?: CellInScript[]; referringCells?: CellInScript[]; meta: PageInfo }> - >(res.data), - ) + const { data } = await explorerService.api.fetchScriptCells(cellType, codeHash, hashType, page, size) const camelCellType = camelcase(cellType) as 'deployedCells' | 'referringCells' if (data == null) { throw new Error('Fetch Cells null') diff --git a/src/pages/Script/index.tsx b/src/pages/Script/index.tsx index c821a523f..b8c325525 100644 --- a/src/pages/Script/index.tsx +++ b/src/pages/Script/index.tsx @@ -3,7 +3,6 @@ import { useHistory } from 'react-router' import { useParams } from 'react-router-dom' import { Tabs } from 'antd' import { useQuery } from 'react-query' -import { AxiosResponse } from 'axios' import { useTranslation } from 'react-i18next' import Content from '../../components/Content' import OverviewCard, { OverviewItemData } from '../../components/Card/OverviewCard' @@ -15,11 +14,11 @@ import { MainnetContractHashTags, TestnetContractHashTags } from '../../constant import { isMainnet } from '../../utils/chain' import { scripts as scriptNameList } from '../ScriptList' import { usePaginationParamsInPage } from '../../utils/hook' -import { shannonToCkb, toCamelcase } from '../../utils/util' +import { shannonToCkb } from '../../utils/util' import DecimalCapacity from '../../components/DecimalCapacity' -import { ScriptInfo, ScriptTabType } from './types' import styles from './styles.module.scss' -import { explorerService, Response } from '../../services/ExplorerService' +import { explorerService } from '../../services/ExplorerService' +import type { ScriptInfo } from '../../services/ExplorerService/fetcher' const scriptDataList = isMainnet() ? MainnetContractHashTags : TestnetContractHashTags @@ -102,12 +101,15 @@ const useSeekScriptName = (codeHash: string, hashType: string): string => { return nameMap.has(`${codeHash}_${hashType}`) ? nameMap.get(`${codeHash}_${hashType}`)! : t('scripts.unnamed_script') } +type ScriptTabType = 'transactions' | 'deployed_cells' | 'referring_cells' | undefined + export const ScriptPage = () => { const history = useHistory() const { t } = useTranslation() const currentLanguage = useCurrentLanguage() - const { codeHash, hashType, tab } = useParams<{ codeHash: string; hashType: string; tab: ScriptTabType }>() + const { codeHash, hashType: _hashType, tab } = useParams<{ codeHash: string; hashType: string; tab: ScriptTabType }>() + const hashType = _hashType === 'data' ? 'data' : 'type' const { currentPage, pageSize } = usePaginationParamsInPage() const [countOfTransactions, setCountOfTransactions] = useState(0) @@ -118,19 +120,14 @@ export const ScriptPage = () => { const [pageOfDeployedCells, setPageOfDeployedCells] = useState(1) const [pageOfReferringCells, setPageOfReferringCells] = useState(1) - const { status, data: resp } = useQuery(['scripts_general_info', codeHash, hashType], () => - explorerService.api.requesterV2.get(`scripts/general_info`, { - params: { - code_hash: codeHash, - hash_type: hashType, - }, - }), + const { status, data: resp } = useQuery(['scripts_general_info', codeHash, hashType], () => + explorerService.api.fetchScriptInfo(codeHash, hashType), ) - const scriptInfo = + const scriptInfo: ScriptInfo = status === 'success' && resp - ? toCamelcase>(resp?.data)!.data - : ({ + ? resp.data + : { id: '-', scriptName: '', scriptType: '', @@ -141,7 +138,7 @@ export const ScriptPage = () => { countOfTransactions: 0, countOfDeployedCells: 0, countOfReferringCells: 0, - } as ScriptInfo) + } scriptInfo.scriptName = useSeekScriptName(scriptInfo.codeHash, scriptInfo.hashType) useEffect(() => { diff --git a/src/pages/Script/types.ts b/src/pages/Script/types.ts deleted file mode 100644 index 178ef53b0..000000000 --- a/src/pages/Script/types.ts +++ /dev/null @@ -1,58 +0,0 @@ -export interface ScriptInfo { - id: string - scriptName: string - scriptType: string - codeHash: string - hashType: 'type' | 'data' - capacityOfDeployedCells: string - capacityOfReferringCells: string - countOfTransactions: number - countOfDeployedCells: number - countOfReferringCells: number -} - -export type ScriptTabType = 'transactions' | 'deployed_cells' | 'referring_cells' | undefined - -export type PageInfo = { - total: number - pageSize: number -} - -export interface CkbTransactionInScript { - id: number - txHash: string - blockId: number - blockNumber: number - blockTimestamp: number - transactionFee: number - isCellbase: boolean - txStatus: string - displayInputs: State.Cell[] - displayOutputs: State.Cell[] -} - -export interface CellInScript { - id: number - capacity: string - data: string - ckbTransactionId: number - createdAt: string - updatedAt: string - status: string - addressId: number - blockId: number - txHash: string - cellIndex: number - generatedById?: number - consumedById?: number - cellType: string - dataSize: number - occupiedCapacity: number - blockTimestamp: number - consumedBlockTimestamp: number - typeHash?: string - udtAmount: number - dao: string - lockScriptId?: number - typeScriptId?: number -} diff --git a/src/pages/SimpleUDT/index.tsx b/src/pages/SimpleUDT/index.tsx index 90a19a043..dc761654e 100644 --- a/src/pages/SimpleUDT/index.tsx +++ b/src/pages/SimpleUDT/index.tsx @@ -51,39 +51,38 @@ export const SimpleUDT = () => { const filter = query.get('filter') const type = query.get('type') - const querySimpleUDT = useQuery(['simple-udt'], async () => { - const wrapper = await explorerService.api.fetchSimpleUDT(typeHash) - const udt = wrapper.attributes - return udt - }) + const querySimpleUDT = useQuery(['simple-udt'], () => explorerService.api.fetchSimpleUDT(typeHash)) const udt = querySimpleUDT.data ?? defaultUDTInfo const { iconFile, typeScript, symbol, uan } = udt const querySimpleUDTTransactions = useQuery( ['simple-udt-transactions', typeHash, currentPage, _pageSize, filter, type], async () => { - const { data, meta } = await explorerService.api.fetchSimpleUDTTransactions({ + const { + data: transactions, + total, + pageSize: resPageSize, + } = await explorerService.api.fetchSimpleUDTTransactions({ typeHash, page: currentPage, size: pageSize, filter, type, }) + + const ensureCellAddrIsNewFormat = (cell: State.Cell) => ({ + ...cell, + addressHash: deprecatedAddrToNewAddr(cell.addressHash), + }) + return { - transactions: - data.map(wrapper => ({ - ...wrapper.attributes, - displayInputs: wrapper.attributes.displayInputs.map(input => ({ - ...input, - addressHash: deprecatedAddrToNewAddr(input.addressHash), - })), - displayOutputs: wrapper.attributes.displayOutputs.map(output => ({ - ...output, - addressHash: deprecatedAddrToNewAddr(output.addressHash), - })), - })) || [], - total: meta?.total ?? 0, - pageSize: meta?.pageSize, + transactions: transactions.map(tx => ({ + ...tx, + displayInputs: tx.displayInputs.map(ensureCellAddrIsNewFormat), + displayOutputs: tx.displayOutputs.map(ensureCellAddrIsNewFormat), + })), + total, + pageSize: resPageSize, } }, ) diff --git a/src/pages/StatisticsChart/activities/AddressBalanceRank.tsx b/src/pages/StatisticsChart/activities/AddressBalanceRank.tsx index 98cc212db..1fbef2947 100644 --- a/src/pages/StatisticsChart/activities/AddressBalanceRank.tsx +++ b/src/pages/StatisticsChart/activities/AddressBalanceRank.tsx @@ -104,11 +104,6 @@ const useOption = () => { } } -const fetchStatisticAddressBalanceRanks = async () => { - const resp = await explorerService.api.fetchStatisticAddressBalanceRank() - return resp.attributes.addressBalanceRanking -} - const toCSV = (statisticAddressBalanceRanks: State.StatisticAddressBalanceRank[]) => statisticAddressBalanceRanks ? statisticAddressBalanceRanks.map(data => [data.ranking, shannonToCkbDecimal(data.balance, 8)]) @@ -146,7 +141,7 @@ export const AddressBalanceRankChart = ({ isThumbnail = false }: { isThumbnail?: description={t('statistic.balance_ranking_description')} isThumbnail={isThumbnail} chartProps={{ onClick: !isThumbnail ? handleClick : undefined }} - fetchData={fetchStatisticAddressBalanceRanks} + fetchData={explorerService.api.fetchStatisticAddressBalanceRank} onFetched={setStatisticAddressBalanceRanks} getEChartOption={getEChartOption} toCSV={toCSV} diff --git a/src/pages/StatisticsChart/activities/BalanceDistribution.tsx b/src/pages/StatisticsChart/activities/BalanceDistribution.tsx index 28557e57c..4b8cb04da 100644 --- a/src/pages/StatisticsChart/activities/BalanceDistribution.tsx +++ b/src/pages/StatisticsChart/activities/BalanceDistribution.tsx @@ -166,9 +166,8 @@ const useOption = ( } const fetchStatisticBalanceDistributions = async () => { - const wrapper = await explorerService.api.fetchStatisticBalanceDistribution() - const balanceDistributionArray = wrapper.attributes.addressBalanceDistribution - const balanceDistributions = balanceDistributionArray.map(distribution => { + const { addressBalanceDistribution } = await explorerService.api.fetchStatisticBalanceDistribution() + const balanceDistributions = addressBalanceDistribution.map(distribution => { const [balance, addresses, sumAddresses] = distribution return { balance, diff --git a/src/pages/StatisticsChart/block/BlockTimeDistribution.tsx b/src/pages/StatisticsChart/block/BlockTimeDistribution.tsx index 0b116ce25..4f143ef2a 100644 --- a/src/pages/StatisticsChart/block/BlockTimeDistribution.tsx +++ b/src/pages/StatisticsChart/block/BlockTimeDistribution.tsx @@ -90,9 +90,7 @@ const useOption = ( } const fetchStatisticBlockTimeDistributions = async () => { - const { - attributes: { blockTimeDistribution }, - } = await explorerService.api.fetchStatisticBlockTimeDistribution() + const { blockTimeDistribution } = await explorerService.api.fetchStatisticBlockTimeDistribution() const sumBlocks = blockTimeDistribution .flatMap(data => Number(data[1])) .reduce((previous, current) => previous + current) diff --git a/src/pages/StatisticsChart/block/EpochTimeDistribution.tsx b/src/pages/StatisticsChart/block/EpochTimeDistribution.tsx index a7ed4b87f..0f347703f 100644 --- a/src/pages/StatisticsChart/block/EpochTimeDistribution.tsx +++ b/src/pages/StatisticsChart/block/EpochTimeDistribution.tsx @@ -97,9 +97,7 @@ const useOption = ( } const fetchStatisticEpochTimeDistributions = async () => { - const { - attributes: { epochTimeDistribution }, - } = await explorerService.api.fetchStatisticEpochTimeDistribution() + const { epochTimeDistribution } = await explorerService.api.fetchStatisticEpochTimeDistribution() const statisticEpochTimeDistributions: State.StatisticEpochTimeDistribution[] = epochTimeDistribution.map(data => { const [time, epoch] = data return { diff --git a/src/pages/StatisticsChart/mining/MinerAddressDistribution.tsx b/src/pages/StatisticsChart/mining/MinerAddressDistribution.tsx index aa58ea77d..8e33f2f91 100644 --- a/src/pages/StatisticsChart/mining/MinerAddressDistribution.tsx +++ b/src/pages/StatisticsChart/mining/MinerAddressDistribution.tsx @@ -89,9 +89,7 @@ const useOption = () => { } const fetchStatisticMinerAddresses = async () => { - const { - attributes: { minerAddressDistribution }, - } = await explorerService.api.fetchStatisticMinerAddressDistribution() + const { minerAddressDistribution } = await explorerService.api.fetchStatisticMinerAddressDistribution() const blockSum = Object.values(minerAddressDistribution).reduce((sum, val) => sum + Number(val), 0) const statisticMinerAddresses: State.StatisticMinerAddress[] = Object.entries(minerAddressDistribution).map( ([key, val]) => ({ diff --git a/src/pages/StatisticsChart/monetary/AnnualPercentageCompensation.tsx b/src/pages/StatisticsChart/monetary/AnnualPercentageCompensation.tsx index 3972873fa..2481af0e2 100644 --- a/src/pages/StatisticsChart/monetary/AnnualPercentageCompensation.tsx +++ b/src/pages/StatisticsChart/monetary/AnnualPercentageCompensation.tsx @@ -94,9 +94,7 @@ const useOption = ( } const fetchStatisticAnnualPercentageCompensations = async () => { - const { - attributes: { nominalApc }, - } = await explorerService.api.fetchStatisticAnnualPercentageCompensation() + const { nominalApc } = await explorerService.api.fetchStatisticAnnualPercentageCompensation() const statisticAnnualPercentageCompensations = nominalApc .filter((_apc, index) => index % 3 === 0 || index === nominalApc.length - 1) .map((apc, index) => ({ diff --git a/src/pages/StatisticsChart/monetary/InflationRate.tsx b/src/pages/StatisticsChart/monetary/InflationRate.tsx index 5f1ec06c8..50542b0ea 100644 --- a/src/pages/StatisticsChart/monetary/InflationRate.tsx +++ b/src/pages/StatisticsChart/monetary/InflationRate.tsx @@ -148,9 +148,8 @@ const useOption = ( } const fetchStatisticInflationRates = async () => { - const { - attributes: { nominalApc, nominalInflationRate, realInflationRate }, - } = await explorerService.api.fetchStatisticInflationRate() + const { nominalApc, nominalInflationRate, realInflationRate } = + await explorerService.api.fetchStatisticInflationRate() const statisticInflationRates = [] for (let i = 0; i < nominalApc.length; i++) { if (i % 6 === 0 || i === nominalApc.length - 1) { diff --git a/src/pages/Tokens/index.tsx b/src/pages/Tokens/index.tsx index 3484bcddb..6476ac738 100644 --- a/src/pages/Tokens/index.tsx +++ b/src/pages/Tokens/index.tsx @@ -102,14 +102,18 @@ export default () => { const sort = new URLSearchParams(location.search).get('sort') const query = useQuery(['tokens', currentPage, _pageSize, sort], async () => { - const { data, meta } = await explorerService.api.fetchTokens(currentPage, _pageSize, sort ?? undefined) - if (data == null || data.length === 0) { + const { + data: tokens, + total, + pageSize, + } = await explorerService.api.fetchTokens(currentPage, _pageSize, sort ?? undefined) + if (tokens.length === 0) { throw new Error('Tokens empty') } return { - total: meta?.total ?? 0, - tokens: data.map(wrapper => wrapper.attributes), - pageSize: meta?.pageSize, + tokens, + total, + pageSize, } }) const total = query.data?.total ?? 0 diff --git a/src/pages/Transaction/TransactionCellScript/index.tsx b/src/pages/Transaction/TransactionCellScript/index.tsx index 5bfba4e20..8ee2185cd 100644 --- a/src/pages/Transaction/TransactionCellScript/index.tsx +++ b/src/pages/Transaction/TransactionCellScript/index.tsx @@ -1,9 +1,9 @@ /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ -import { useEffect, useState, ReactNode, useRef } from 'react' +import { useState, ReactNode, useRef } from 'react' import BigNumber from 'bignumber.js' -import { TFunction, useTranslation } from 'react-i18next' -import { explorerService, Response } from '../../../services/ExplorerService' -import { CellState } from '../../../constants/common' +import { useTranslation } from 'react-i18next' +import { useQuery } from 'react-query' +import { explorerService } from '../../../services/ExplorerService' import { hexToUtf8 } from '../../../utils/string' import { TransactionDetailCopyButton, @@ -14,7 +14,7 @@ import { TransactionCellDetailPanel, TransactionDetailData, TransactionDetailCapacityUsage, - TransactionCellScriptContentPanel, + TransactionCellInfoValuePanel, TransactionDetailScriptButton, } from './styled' import SmallLoading from '../../../components/Loading/SmallLoading' @@ -27,10 +27,38 @@ import { ReactComponent as OuterLinkIcon } from '../../../assets/outer_link_icon import { HelpTip } from '../../../components/HelpTip' import { useSetToast } from '../../../components/Toast' import { CellBasicInfo } from '../../../utils/transformer' +import { isAxiosError } from '../../../utils/error' -const initScriptContent = { - lock: 'null', - type: 'null', +enum CellInfo { + LOCK = 1, + TYPE = 2, + DATA = 3, + CAPACITY = 4, +} + +type CapacityUsage = Record<'declared' | 'occupied', string | null> + +interface CellData { + data: string +} + +type CellInfoValue = State.Script | CellData | CapacityUsage | null | undefined + +function isScript(content: CellInfoValue): content is State.Script { + return content != null && 'codeHash' in content +} + +function isCapacityUsage(content: CellInfoValue): content is CapacityUsage { + return content != null && 'declared' in content +} + +function isCellData(content: CellInfoValue): content is CellData { + return content != null && 'data' in content +} + +const initCellInfoValue = { + lock: null, + type: null, data: { data: '0x', }, @@ -41,11 +69,9 @@ type TransactionCellScriptProps = { onClose: Function } -type CapacityUsage = Record<'declared' | 'occupied', string | null> - -const updateJsonFormat = (content: State.Script | State.Data | CapacityUsage | null): string => { - if (content !== null && (content as State.Script).args !== undefined) { - const { codeHash, args, hashType } = content as State.Script +const getContentJSONWithSnakeCase = (content: CellInfoValue): string => { + if (isScript(content)) { + const { codeHash, args, hashType } = content return JSON.stringify( { code_hash: codeHash, @@ -59,199 +85,158 @@ const updateJsonFormat = (content: State.Script | State.Data | CapacityUsage | n return JSON.stringify(content, null, 4) } -const handleFetchCellInfo = async ( - cell: CellBasicInfo, - state: CellState, - setScriptFetchStatus: (val: boolean) => void, - setContent: Function, - setToast: ReturnType, - t: TFunction, -) => { - setScriptFetchStatus(false) - +const fetchCellInfo = async (cell: CellBasicInfo, state: CellInfo): Promise => { const fetchLock = async () => { if (cell.id) { - const wrapper: Response.Wrapper | null = await explorerService.api.fetchScript( - 'lock_scripts', - `${cell.id}`, - ) - return wrapper ? wrapper.attributes : initScriptContent.lock + const wrapper = await explorerService.api.fetchScript('lock_scripts', `${cell.id}`) + return wrapper ? wrapper.attributes : initCellInfoValue.lock } - return initScriptContent.lock + return initCellInfoValue.lock } const fetchType = async () => { if (cell.id) { - const wrapper: Response.Wrapper | null = await explorerService.api.fetchScript( - 'type_scripts', - `${cell.id}`, - ) - return wrapper ? wrapper.attributes : initScriptContent.type + const wrapper = await explorerService.api.fetchScript('type_scripts', `${cell.id}`) + return wrapper ? wrapper.attributes : initCellInfoValue.type } - return initScriptContent.type + return initCellInfoValue.type } const fetchData = async () => { - if (cell.id) { - return explorerService.api - .fetchCellData(`${cell.id}`) - .then((wrapper: Response.Wrapper | null) => { - const dataValue: State.Data = wrapper ? wrapper.attributes : initScriptContent.data - if (wrapper && cell.isGenesisOutput) { - dataValue.data = hexToUtf8(wrapper.attributes.data) - } - return dataValue || initScriptContent.data - }) - .catch(error => { - if (error.response && error.response.data && error.response.data[0]) { - const err = error.response.data[0] - if (err.status === 400 && err.code === 1022) { - setToast({ - message: t('toast.data_too_large'), - type: 'warning', - }) - return null - } - } - return null - }) + // TODO: When will cell.id be empty? Its type description indicates that it will not be empty. + if (!cell.id) return initCellInfoValue.data + + let dataValue = await explorerService.api.fetchCellData(`${cell.id}`) + if (!dataValue) return initCellInfoValue.data + + if (cell.isGenesisOutput) { + dataValue = hexToUtf8(dataValue) } - const dataValue: State.Data = initScriptContent.data - return dataValue + return { data: dataValue } } switch (state) { - case CellState.LOCK: - fetchLock().then(lock => { - setScriptFetchStatus(true) - setContent(lock) - }) - break - case CellState.TYPE: - fetchType().then(type => { - setScriptFetchStatus(true) - setContent(type) - }) - break - case CellState.DATA: - fetchData().then(data => { - setScriptFetchStatus(true) - setContent(data) - }) - break - case CellState.CAPACITY: - { - setScriptFetchStatus(true) - const declared = new BigNumber(cell.capacity) - const occupied = new BigNumber(cell.occupiedCapacity) - - setContent({ - declared: `${localeNumberString(declared.dividedBy(10 ** 8))} CKBytes`, - occupied: `${localeNumberString(occupied.dividedBy(10 ** 8))} CKBytes`, - }) + case CellInfo.LOCK: + return fetchLock() + + case CellInfo.TYPE: + return fetchType() + + case CellInfo.DATA: + return fetchData() + + case CellInfo.CAPACITY: { + const declared = new BigNumber(cell.capacity) + const occupied = new BigNumber(cell.occupiedCapacity) + + return { + declared: `${localeNumberString(declared.dividedBy(10 ** 8))} CKBytes`, + occupied: `${localeNumberString(occupied.dividedBy(10 ** 8))} CKBytes`, } + } - break default: - break + return null } } -const ScriptContentItem = ({ title = '', value = '' }: { title?: string; value?: ReactNode | string }) => ( +const JSONKeyValueView = ({ title = '', value = '' }: { title?: string; value?: ReactNode | string }) => (
{title}
{value}
) -const ScriptContent = ({ - content, - state, -}: { - content: State.Script | State.Data | CapacityUsage | undefined - state: CellState -}) => { +const CellInfoValueRender = ({ content }: { content: CellInfoValue }) => { const { t } = useTranslation() - const hashTag = getContractHashTag(content as State.Script) - const data = content as State.Data - const script = content as State.Script - - if (state === CellState.CAPACITY) { - const capacities = content as CapacityUsage + if (isScript(content)) { + const hashTag = getContractHashTag(content) return ( <> - {Object.keys(capacities).map(key => { - const v = capacities[key as keyof CapacityUsage] + + {hashTag && ( + + + + } + /> + )} + + + + ) + } - if (!v) return null + if (isCapacityUsage(content)) { + return ( + <> + {Object.entries(content).map(([key, value]) => { const field = t(`transaction.${key}_capacity`) - return + return })} ) } - if (state === CellState.DATA) { + + if (isCellData(content)) { return ( - ) } - if (!script.args) { - return - } - return ( - <> - - {hashTag && ( - - - - } - /> - )} - - - - ) + + return } -const ScriptContentJson = ({ - content, - state, -}: { - content: State.Script | State.Data | CapacityUsage | undefined - state: CellState -}) => ( - +const CellInfoValueJSONView = ({ content, state }: { content: CellInfoValue; state: CellInfo }) => ( + {'{'} - + {'}'} - + ) export default ({ cell, onClose }: TransactionCellScriptProps) => { const setToast = useSetToast() const { t } = useTranslation() - const [scriptFetched, setScriptFetched] = useState(false) - const [content, setContent] = useState(null as State.Script | State.Data | CapacityUsage | null) - const [state, setState] = useState(CellState.LOCK as CellState) + const [selectedInfo, setSelectedInfo] = useState(CellInfo.LOCK) const ref = useRef(null) - const changeType = (newState: CellState) => { - setState(state !== newState ? newState : state) + const changeType = (newState: CellInfo) => { + setSelectedInfo(selectedInfo !== newState ? newState : selectedInfo) } - useEffect(() => { - handleFetchCellInfo(cell, state, setScriptFetched, setContent, setToast, t) - }, [cell, state, setToast, t]) + const { data: content, isFetched } = useQuery( + ['cell-info', cell, selectedInfo], + () => + fetchCellInfo(cell, selectedInfo).catch(error => { + if (!isAxiosError(error)) return null + const respErrors = error.response?.data + if (!Array.isArray(respErrors)) return null + + const err = respErrors[0] + if (err.status === 400 && err.code === 1022) { + setToast({ + message: t('toast.data_too_large'), + type: 'warning', + }) + } + + return null + }), + { + retry: false, + refetchInterval: false, + }, + ) const onClickCopy = () => { - navigator.clipboard.writeText(updateJsonFormat(content)).then( + navigator.clipboard.writeText(getContentJSONWithSnakeCase(content)).then( () => { setToast({ message: t('common.copied') }) }, @@ -264,24 +249,25 @@ export default ({ cell, onClose }: TransactionCellScriptProps) => { return ( - changeType(CellState.LOCK)}> + changeType(CellInfo.LOCK)}> {t('transaction.lock_script')} - changeType(CellState.TYPE)}> + changeType(CellInfo.TYPE)}> {t('transaction.type_script')} - changeType(CellState.DATA)}> + changeType(CellInfo.DATA)}> {t('transaction.data')} changeType(CellState.CAPACITY)} + selected={selectedInfo === CellInfo.CAPACITY} + onClick={() => changeType(CellInfo.CAPACITY)} > {t('transaction.capacity_usage')} +
close icon {}} onClick={() => onClose()} />
@@ -290,24 +276,22 @@ export default ({ cell, onClose }: TransactionCellScriptProps) => {
- {content && scriptFetched ? ( + {isFetched ? (
- +
) : ( -
{!scriptFetched ? : null}
+
{!isFetched ? : null}
)} - {!content && scriptFetched ? null : ( + + {!isFetched || !content ? null : (
{t('common.copy')}
- {(state === CellState.LOCK || state === CellState.TYPE) && - content && - typeof content === 'object' && - 'codeHash' in content && - 'hashType' in content ? ( + + {isScript(content) ? (
{t('scripts.script')}
diff --git a/src/pages/Transaction/TransactionCellScript/styled.tsx b/src/pages/Transaction/TransactionCellScript/styled.tsx index 4b1585d4a..7aec2f317 100644 --- a/src/pages/Transaction/TransactionCellScript/styled.tsx +++ b/src/pages/Transaction/TransactionCellScript/styled.tsx @@ -256,7 +256,7 @@ export const TransactionDetailScriptButton = styled.a` } ` -export const TransactionCellScriptContentPanel = styled.div` +export const TransactionCellInfoValuePanel = styled.div` > div { display: flex; margin: 2px 0 2px 30px; diff --git a/src/pages/Transaction/TransactionComp/TransactionLite/TransactionLite.tsx b/src/pages/Transaction/TransactionComp/TransactionLite/TransactionLite.tsx index 35641a5b0..6488c43fe 100644 --- a/src/pages/Transaction/TransactionComp/TransactionLite/TransactionLite.tsx +++ b/src/pages/Transaction/TransactionComp/TransactionLite/TransactionLite.tsx @@ -10,7 +10,7 @@ import { shannonToCkb } from '../../../../utils/util' import { Addr } from '../../TransactionCell' import { defaultTransactionLiteDetails } from '../../state' import { TransactionBadge } from './TransactionBadge' -import { fetchTransactionLiteDetailsByHash } from '../../../../services/ExplorerService/fetcher' +import { explorerService } from '../../../../services/ExplorerService' import { useIsMobile } from '../../../../utils/hook' const getTransferItemTag = (transfer: State.LiteTransfer) => { @@ -41,7 +41,7 @@ export const TransactionCompLite: FC<{ isCellbase: boolean }> = ({ isCellbase }) const isMobile = useIsMobile() const query = useQuery(['ckb_transaction_details', txHash], async () => { - const ckbTransactionDetails = await fetchTransactionLiteDetailsByHash(txHash) + const ckbTransactionDetails = await explorerService.api.fetchTransactionLiteDetailsByHash(txHash) return ckbTransactionDetails.data }) const transactionLiteDetails: State.TransactionLiteDetails[] = query.data ?? defaultTransactionLiteDetails diff --git a/src/pages/Transaction/index.tsx b/src/pages/Transaction/index.tsx index ad7511194..1ce404445 100644 --- a/src/pages/Transaction/index.tsx +++ b/src/pages/Transaction/index.tsx @@ -19,8 +19,8 @@ export default () => { const { hash: txHash } = useParams<{ hash: string }>() const query = useQuery(['transaction', txHash], async () => { - const wrapper = await explorerService.api.fetchTransactionByHash(txHash) - const transaction = wrapper.attributes + const transaction = await explorerService.api.fetchTransactionByHash(txHash) + // TODO: When will displayOutputs be empty? Its type description indicates that it will not be empty. if (transaction.displayOutputs && transaction.displayOutputs.length > 0) { transaction.displayOutputs[0].isGenesisOutput = transaction.blockNumber === 0 } diff --git a/src/pages/TransactionList/index.tsx b/src/pages/TransactionList/index.tsx index bf87919ed..520be2039 100644 --- a/src/pages/TransactionList/index.tsx +++ b/src/pages/TransactionList/index.tsx @@ -242,19 +242,17 @@ const TransactionsPanel: FC<{ type: TxStatus }> = ({ type }) => { const [, type] = queryKey switch (type) { case 'pending': { - const resp = await explorerService.api.fetchPendingTransactions(currentPage, pageSize, sort) - return { - transactions: resp.data, - total: resp.meta?.total ?? 0, - } + const { data: transactions, total } = await explorerService.api.fetchPendingTransactions( + currentPage, + pageSize, + sort, + ) + return { transactions, total } } case 'confirmed': default: { - const resp = await explorerService.api.fetchTransactions(currentPage, pageSize, sort) - return { - transactions: resp.data.map(wrapper => wrapper.attributes) ?? [], - total: resp.meta?.total ?? 0, - } + const { data: transactions, total } = await explorerService.api.fetchTransactions(currentPage, pageSize, sort) + return { transactions, total } } } }, diff --git a/src/service/app/blockchain.ts b/src/service/app/blockchain.ts index bc8374718..9fe77a350 100644 --- a/src/service/app/blockchain.ts +++ b/src/service/app/blockchain.ts @@ -1,27 +1,12 @@ -import { explorerService, Response } from '../../services/ExplorerService' +import { explorerService } from '../../services/ExplorerService' import { setChainAlerts } from '../../components/Sheet' -const alertNotEmpty = (wrapper: Response.Wrapper | null): boolean => - wrapper !== null && - wrapper !== undefined && - wrapper.attributes && - wrapper.attributes.blockchainInfo && - wrapper.attributes.blockchainInfo.alerts && - wrapper.attributes.blockchainInfo.alerts.length > 0 - const ALERT_TO_FILTER_OUT = 'CKB v0.105.* have bugs. Please upgrade to the latest version.' export const handleBlockchainAlert = () => { - explorerService.api.fetchBlockchainInfo().then((wrapper: Response.Wrapper | null) => { - if (alertNotEmpty(wrapper)) { - setChainAlerts( - wrapper!.attributes.blockchainInfo.alerts - .map(alert => alert.message) - .filter(msg => msg !== ALERT_TO_FILTER_OUT), - ) - } else { - setChainAlerts([]) - } + explorerService.api.fetchBlockchainInfo().then(wrapper => { + const alerts = wrapper?.attributes.blockchainInfo.alerts ?? [] + setChainAlerts(alerts.map(alert => alert.message).filter(msg => msg !== ALERT_TO_FILTER_OUT)) }) } diff --git a/src/service/app/charts/cache.ts b/src/service/app/charts/cache.ts index 5f189fb45..3a3402e49 100644 --- a/src/service/app/charts/cache.ts +++ b/src/service/app/charts/cache.ts @@ -1,17 +1,16 @@ -import { explorerService, Response } from '../../../services/ExplorerService' +import { explorerService } from '../../../services/ExplorerService' import { removeCachedData, fetchCachedData } from '../../../utils/cache' import { ChartCachedKeys } from '../../../constants/cache' -export const flushCacheInfo = () => { - explorerService.api.fetchFlushChartCache().then((wrapper: Response.Wrapper | null) => { - if (wrapper && wrapper.attributes.flushCacheInfo.length > 0) { - // eslint-disable-next-line no-restricted-syntax - for (const [, value] of Object.entries(ChartCachedKeys)) { - removeCachedData(fetchCachedData(value) as string) - removeCachedData(value) - } - } - }) +export const flushCacheInfo = async () => { + const { flushCacheInfo } = await explorerService.api.fetchFlushChartCache() + if (flushCacheInfo.length === 0) return + + // eslint-disable-next-line no-restricted-syntax + for (const [, value] of Object.entries(ChartCachedKeys)) { + removeCachedData(fetchCachedData(value) as string) + removeCachedData(value) + } } export default flushCacheInfo diff --git a/src/services/ExplorerService/fetcher.ts b/src/services/ExplorerService/fetcher.ts index 18310791c..8687340a7 100644 --- a/src/services/ExplorerService/fetcher.ts +++ b/src/services/ExplorerService/fetcher.ts @@ -1,519 +1,776 @@ import { AxiosResponse } from 'axios' import BigNumber from 'bignumber.js' import { Dayjs } from 'dayjs' +import { ReactNode } from 'react' import { pick } from '../../utils/object' import { toCamelcase } from '../../utils/util' import { requesterV1, requesterV2 } from './requester' import { Response } from './types' +import { assert } from '../../utils/error' -// TODO: Temporarily compatible with old code, it exists in the form of `explorerService.api.requesterV2`, -// and in the future, the `requester` should be hidden from the outside. -export { requesterV2 } +async function v1Get(...args: Parameters) { + return requesterV1.get(...args).then(res => toCamelcase>(res.data)) +} + +const v1GetWrapped = (...args: Parameters) => v1Get>(...args).then(res => res.data) + +const v1GetNullableWrapped = (...args: Parameters) => + v1Get | null>(...args).then(res => res.data) + +const v1GetWrappedList = (...args: Parameters) => + v1Get[]>(...args).then(res => res.data) + +const v1GetUnwrapped = (...args: Parameters) => + v1GetWrapped(...args).then(wrapper => wrapper.attributes) + +const v1GetUnwrappedList = (...args: Parameters) => + v1GetWrappedList(...args).then(wrappers => wrappers.map(wrapper => wrapper.attributes)) -export const fetchBlocks = (page: number, size: number, sort?: string) => - requesterV1 - .get('blocks', { +const v1GetUnwrappedPagedList = (...args: Parameters) => + v1Get[]>(...args).then(res => { + assert(res.meta, 'Unexpected paged list response') + return { + data: res.data.map(wrapper => wrapper.attributes), + ...res.meta, + } + }) + +export enum SearchResultType { + Block = 'block', + Transaction = 'ckb_transaction', + Address = 'address', + LockHash = 'lock_hash', + UDT = 'udt', +} + +export const apiFetcher = { + fetchBlocks: (page: number, size: number, sort?: string) => + v1Get[]>('blocks', { params: { page, page_size: size, sort, }, - }) - .then((res: AxiosResponse) => toCamelcase[]>>(res.data)) - -export const fetchLatestBlocks = (size: number) => fetchBlocks(1, size) - -export const fetchAddressInfo = (address: string) => - requesterV1 - .get(`addresses/${address}`) - .then((res: AxiosResponse) => toCamelcase>(res.data.data)) - -export const fetchTransactionsByAddress = ( - address: string, - page: number, - size: number, - sort?: string, - txTypeFilter?: string, -) => - requesterV1 - .get(`address_transactions/${address}`, { + }), + + fetchLatestBlocks: (size: number) => apiFetcher.fetchBlocks(1, size), + + fetchAddressInfo: (address: string) => + v1GetWrapped(`addresses/${address}`).then( + (wrapper): State.Address => ({ + ...wrapper.attributes, + type: wrapper.type === 'lock_hash' ? 'LockHash' : 'Address', + }), + ), + + fetchTransactionsByAddress: (address: string, page: number, size: number, sort?: string, txTypeFilter?: string) => + v1GetUnwrappedPagedList(`address_transactions/${address}`, { params: { page, page_size: size, sort, tx_type: txTypeFilter, }, - }) - .then((res: AxiosResponse) => toCamelcase[]>>(res.data)) + }), + + fetchTransactionRaw: (hash: string) => requesterV2.get(`transactions/${hash}/raw`).then(res => res.data), -export const fetchTransactionByHash = (hash: string) => - requesterV1 - .get(`transactions/${hash}`) - .then((res: AxiosResponse) => toCamelcase>(res.data.data)) + fetchTransactionByHash: (hash: string) => v1GetUnwrapped(`transactions/${hash}`), -export const fetchTransactionLiteDetailsByHash = (hash: string) => - requesterV2 - .get(`ckb_transactions/${hash}/details`) - .then((res: AxiosResponse) => toCamelcase>(res.data)) + fetchTransactionLiteDetailsByHash: (hash: string) => + requesterV2 + .get(`ckb_transactions/${hash}/details`) + .then((res: AxiosResponse) => toCamelcase>(res.data)), -export const fetchTransactions = (page: number, size: number, sort?: string) => - requesterV1 - .get('transactions', { + fetchTransactions: (page: number, size: number, sort?: string) => + v1GetUnwrappedPagedList('transactions', { params: { page, page_size: size, sort, }, - }) - .then((res: AxiosResponse) => toCamelcase[]>>(res.data)) + }), -export const fetchLatestTransactions = (size: number) => fetchTransactions(1, size) + fetchLatestTransactions: (size: number) => apiFetcher.fetchTransactions(1, size), -export const fetchPendingTransactions = (page: number, size: number, sort?: string) => - requesterV2 - .get('pending_transactions', { - params: { - page, - page_size: size, - sort, - }, - }) - .then(res => toCamelcase>(res.data)) + fetchPendingTransactions: (page: number, size: number, sort?: string) => + requesterV2 + .get('pending_transactions', { + params: { + page, + page_size: size, + sort, + }, + }) + .then(res => toCamelcase>(res.data)) + .then(res => { + assert(res.meta, 'Unexpected paged list response') + return { + data: res.data, + ...res.meta, + } + }), -export const fetchPendingTransactionsCount = () => fetchPendingTransactions(1, 1).then(resp => resp.meta?.total) + fetchPendingTransactionsCount: () => apiFetcher.fetchPendingTransactions(1, 1).then(res => res.total), -export const fetchTransactionsByBlockHash = ( - blockHash: string, - { - page, - size: page_size, - filter, - }: Partial<{ - page: number - size: number - filter: string | null - }>, -) => - requesterV1 - .get(`/block_transactions/${blockHash}`, { + fetchTransactionsByBlockHash: ( + blockHash: string, + { + page, + size: page_size, + filter, + }: Partial<{ + page: number + size: number + filter: string | null + }>, + ) => + v1GetUnwrappedPagedList(`/block_transactions/${blockHash}`, { params: { page, page_size, address_hash: filter?.startsWith('ck') ? filter : null, tx_hash: filter?.startsWith('0x') ? filter : null, }, - }) - .then((res: AxiosResponse) => toCamelcase[]>>(res.data)) - -export const fetchBlock = (blockHeightOrHash: string) => - requesterV1 - .get(`blocks/${blockHeightOrHash}`) - .then((res: AxiosResponse) => toCamelcase>(res.data.data)) - -export const fetchScript = (scriptType: 'lock_scripts' | 'type_scripts', id: string) => - requesterV1 - .get(`/cell_output_${scriptType}/${id}`) - .then((res: AxiosResponse) => toCamelcase>(res.data.data)) - -export const fetchCellData = (id: string) => - requesterV1 - .get(`/cell_output_data/${id}`) - .then((res: AxiosResponse) => toCamelcase>(res.data.data)) - -export const fetchSearchResult = (param: string) => - requesterV1 - .get('suggest_queries', { - params: { - q: param, - }, - }) - .then((res: AxiosResponse) => toCamelcase(res.data)) + }), -export const fetchStatistics = () => - requesterV1 - .get('statistics') - .then((res: AxiosResponse) => toCamelcase>(res.data.data)) + fetchBlock: (blockHeightOrHash: string) => v1GetUnwrapped(`blocks/${blockHeightOrHash}`), -export const fetchStatisticInfo = (infoName: string) => - requesterV1.get(`statistics/${infoName}`).then((res: AxiosResponse) => res.data) + fetchScript: (scriptType: 'lock_scripts' | 'type_scripts', id: string) => + v1GetNullableWrapped(`/cell_output_${scriptType}/${id}`), -export const fetchTipBlockNumber = () => - fetchStatisticInfo('tip_block_number').then(wrapper => toCamelcase>(wrapper.data)) + fetchCellData: (id: string) => + // TODO: When will it return an empty result? + v1GetNullableWrapped<{ data: string }>(`/cell_output_data/${id}`).then(res => res?.attributes.data ?? null), + + fetchSearchResult: (param: string) => + v1Get< + | Response.Wrapper + | Response.Wrapper + | Response.Wrapper + | Response.Wrapper + | Response.Wrapper + >('suggest_queries', { + params: { + q: param, + }, + }), + + fetchStatistics: () => v1GetUnwrapped(`statistics`), + + fetchTipBlockNumber: () => + v1GetUnwrapped>('statistics/tip_block_number').then( + statistics => statistics.tipBlockNumber, + ), + + fetchBlockchainInfo: () => + v1GetNullableWrapped<{ + blockchainInfo: { + isInitialBlockDownload: boolean + epoch: string + difficulty: string + medianTime: string + chain: string + alerts: { + id: string + message: string + noticeNntil: string + priority: string + }[] + } + }>('statistics/blockchain_info'), + + fetchNodeVersion: () => v1GetUnwrapped<{ version: string }>('/nets/version'), + + fetchNervosDao: () => v1GetUnwrapped(`contracts/nervos_dao`), + + // Unused currently + fetchNervosDaoTransactions: (page: number, size: number) => + v1Get[]>(`contract_transactions/nervos_dao`, { + params: { + page, + page_size: size, + }, + }), -export const fetchBlockchainInfo = () => - fetchStatisticInfo('blockchain_info').then(wrapper => - toCamelcase>(wrapper.data), - ) + // Unused currently + fetchNervosDaoTransactionsByHash: (hash: string) => + v1GetWrapped(`dao_contract_transactions/${hash}`), -export const fetchNodeVersion = () => - requesterV1('/nets/version').then((res: AxiosResponse) => - toCamelcase>(res.data.data), - ) + // Unused currently + fetchNervosDaoTransactionsByAddress: (address: string, page: number, size: number) => + v1Get[]>(`address_dao_transactions/${address}`, { + params: { + page, + page_size: size, + }, + }), -export const fetchNervosDao = () => - requesterV1('/contracts/nervos_dao').then((res: AxiosResponse) => - toCamelcase>(res.data.data), - ) + fetchNervosDaoTransactionsByFilter: ({ page, size, filter }: { page: number; size: number; filter?: string }) => + v1GetUnwrappedPagedList(`contract_transactions/nervos_dao`, { + params: { + page, + page_size: size, + tx_hash: filter?.startsWith('0x') ? filter : null, + address_hash: filter?.startsWith('0x') ? null : filter, + }, + }), -export const fetchNervosDaoTransactions = (page: number, size: number) => - requesterV1('/contract_transactions/nervos_dao', { - params: { - page, - page_size: size, - }, - }).then((res: AxiosResponse) => toCamelcase[]>>(res.data)) - -export const fetchNervosDaoTransactionsByHash = (hash: string) => - requesterV1(`/dao_contract_transactions/${hash}`).then((res: AxiosResponse) => - toCamelcase>(res.data.data), - ) - -export const fetchNervosDaoTransactionsByAddress = (address: string, page: number, size: number) => - requesterV1(`/address_dao_transactions/${address}`, { - params: { - page, - page_size: size, - }, - }).then((res: AxiosResponse) => toCamelcase[]>>(res.data)) - -export const fetchNervosDaoTransactionsByFilter = ({ - page, - size, - filter, -}: { - page: number - size: number - filter?: string -}) => - requesterV1(`/contract_transactions/nervos_dao`, { - params: { - page, - page_size: size, - tx_hash: filter?.startsWith('0x') ? filter : null, - address_hash: filter?.startsWith('0x') ? null : filter, - }, - }).then((res: AxiosResponse) => toCamelcase[]>>(res.data)) - -export const fetchNervosDaoDepositors = () => - requesterV1(`/dao_depositors`).then((res: AxiosResponse) => - toCamelcase[]>>(res.data), - ) - -export const fetchStatisticTransactionCount = () => - requesterV1(`/daily_statistics/transactions_count`).then((res: AxiosResponse) => { - const resp = toCamelcase[]>>(res.data) - return { - ...resp, - // filter latest exceptional data out - data: resp.data.filter((item, idx) => idx < resp.data.length - 2 || item.attributes.transactionsCount !== '0'), - } - }) + fetchNervosDaoDepositors: () => v1GetUnwrappedList(`/dao_depositors`), -export const fetchStatisticAddressCount = () => - requesterV1(`/daily_statistics/addresses_count`).then((res: AxiosResponse) => { - const resp = toCamelcase[]>>(res.data) - return { - ...resp, + fetchStatisticTransactionCount: () => + v1GetUnwrappedList(`/daily_statistics/transactions_count`).then(items => // filter latest exceptional data out - data: resp.data.filter((item, idx) => idx < resp.data.length - 2 || item.attributes.addressesCount !== '0'), - } - }) + items.filter((item, idx) => idx < items.length - 2 || item.transactionsCount !== '0'), + ), -export const fetchStatisticTotalDaoDeposit = () => - requesterV1(`/daily_statistics/total_depositors_count-total_dao_deposit`).then((res: AxiosResponse) => - toCamelcase[]>>(res.data), - ) - -export const fetchStatisticNewDaoDeposit = () => - requesterV1(`/daily_statistics/daily_dao_deposit-daily_dao_depositors_count`).then((res: AxiosResponse) => - toCamelcase[]>>(res.data), - ) - -export const fetchStatisticNewDaoWithdraw = () => - requesterV1(`/daily_statistics/daily_dao_withdraw`).then((res: AxiosResponse) => - toCamelcase[]>>(res.data), - ) - -export const fetchStatisticCirculationRatio = () => - requesterV1(`/daily_statistics/circulation_ratio`).then((res: AxiosResponse) => - toCamelcase[]>>(res.data), - ) - -export const fetchStatisticDifficultyHashRate = () => - requesterV1(`/epoch_statistics/difficulty-uncle_rate-hash_rate`).then((res: AxiosResponse) => { - const resp = toCamelcase[]>>(res.data) - return { - ...resp, - data: resp.data - // filter latest exceptional data out - .filter((item, idx) => idx < resp.data.length - 2 || item.attributes.hashRate !== '0.0') - .map(wrapper => ({ - ...wrapper, - attributes: { - // Data may enter the cache, so it is purify to reduce volume. - ...pick(wrapper.attributes, ['difficulty', 'epochNumber']), - uncleRate: new BigNumber(wrapper.attributes.uncleRate).toFixed(4), - hashRate: new BigNumber(wrapper.attributes.hashRate).multipliedBy(1000).toString(), - }, - })), - } - }) - -export const fetchStatisticDifficulty = () => - requesterV1(`/daily_statistics/avg_difficulty`).then((res: AxiosResponse) => { - const resp = toCamelcase[]>>(res.data) - return { - ...resp, + fetchStatisticAddressCount: () => + v1GetUnwrappedList(`/daily_statistics/addresses_count`).then(items => // filter latest exceptional data out - data: resp.data.filter((item, idx) => idx < resp.data.length - 2 || item.attributes.avgDifficulty !== '0.0'), - } - }) + items.filter((item, idx) => idx < items.length - 2 || item.addressesCount !== '0'), + ), -export const fetchStatisticHashRate = () => - requesterV1(`/daily_statistics/avg_hash_rate`).then((res: AxiosResponse) => { - const resp = toCamelcase[]>>(res.data) - return { - ...resp, - data: resp.data - // filter latest exceptional data out - .filter((item, idx) => idx < resp.data.length - 2 || item.attributes.avgHashRate !== '0.0') - .map(wrapper => ({ - ...wrapper, - attributes: { - ...wrapper.attributes, - avgHashRate: new BigNumber(wrapper.attributes.avgHashRate).multipliedBy(1000).toString(), - }, - })), - } - }) + fetchStatisticTotalDaoDeposit: () => + v1GetUnwrappedList('/daily_statistics/total_depositors_count-total_dao_deposit'), -export const fetchStatisticUncleRate = () => - requesterV1(`/daily_statistics/uncle_rate`).then((res: AxiosResponse) => { - const resp = toCamelcase[]>>(res.data) - return { - ...resp, - data: resp.data - // filter latest exceptional data out - .filter((item, idx) => idx < resp.data.length - 2 || item.attributes.uncleRate !== '0') - .map(wrapper => ({ - ...wrapper, - attributes: { - ...wrapper.attributes, - uncleRate: new BigNumber(wrapper.attributes.uncleRate).toFixed(4), - }, - })), - } - }) + fetchStatisticNewDaoDeposit: () => + v1GetUnwrappedList('/daily_statistics/daily_dao_deposit-daily_dao_depositors_count'), -export const fetchStatisticMinerAddressDistribution = () => - requesterV1(`/distribution_data/miner_address_distribution`).then((res: AxiosResponse) => - toCamelcase>(res.data.data), - ) + // Unused currently + fetchStatisticNewDaoWithdraw: () => + v1GetUnwrappedList('/daily_statistics/daily_dao_withdraw'), -export const fetchStatisticMinerVersionDistribution = () => - requesterV2(`/blocks/ckb_node_versions`).then((res: AxiosResponse) => - toCamelcase<{ data: Array<{ version: string; blocksCount: number }> }>(res.data), - ) + fetchStatisticCirculationRatio: () => + v1GetUnwrappedList('/daily_statistics/circulation_ratio'), -export const fetchStatisticCellCount = (): Promise[]>> => - requesterV1(`/daily_statistics/live_cells_count-dead_cells_count`).then(res => { - const resp = toCamelcase>[]>>( - res.data, - ) - return { - ...resp, - data: resp.data.map(wrapper => ({ - ...wrapper, - attributes: { - ...wrapper.attributes, - allCellsCount: ( - Number(wrapper.attributes.liveCellsCount) + Number(wrapper.attributes.deadCellsCount) - ).toString(), - }, - })), - } - }) - -export const fetchStatisticDifficultyUncleRateEpoch = () => - requesterV1(`/epoch_statistics/epoch_time-epoch_length`).then((res: AxiosResponse) => { - const resp = toCamelcase[]>>(res.data) - return { - ...resp, - data: resp.data.map(wrapper => ({ - ...wrapper, - // Data may enter the cache, so it is purify to reduce volume. - attributes: pick(wrapper.attributes, ['epochNumber', 'epochTime', 'epochLength']), - })), - } - }) - -export const fetchStatisticAddressBalanceRank = () => - requesterV1(`/statistics/address_balance_ranking`).then((res: AxiosResponse) => - toCamelcase>(res.data.data), - ) - -export const fetchStatisticBalanceDistribution = () => - requesterV1(`/distribution_data/address_balance_distribution`).then((res: AxiosResponse) => - toCamelcase>(res.data.data), - ) - -export const fetchStatisticTxFeeHistory = () => - requesterV1(`/daily_statistics/total_tx_fee`).then((res: AxiosResponse) => - toCamelcase[]>>(res.data), - ) - -export const fetchStatisticBlockTimeDistribution = () => - requesterV1(`/distribution_data/block_time_distribution`).then((res: AxiosResponse) => - toCamelcase>(res.data.data), - ) - -export const fetchStatisticAverageBlockTimes = () => - requesterV1(`/distribution_data/average_block_time`).then((res: AxiosResponse) => { - const { - attributes: { averageBlockTime }, - } = toCamelcase>(res.data.data) - return averageBlockTime - }) + fetchStatisticDifficultyHashRate: () => + v1GetUnwrappedList(`/epoch_statistics/difficulty-uncle_rate-hash_rate`).then( + items => { + return ( + items + // filter latest exceptional data out + .filter((item, idx) => idx < items.length - 2 || item.hashRate !== '0.0') + // Data may enter the cache, so it is purify to reduce volume. + .map(item => ({ + ...pick(item, ['difficulty', 'epochNumber']), + uncleRate: new BigNumber(item.uncleRate).toFixed(4), + hashRate: new BigNumber(item.hashRate).multipliedBy(1000).toString(), + })) + ) + }, + ), -export const fetchStatisticOccupiedCapacity = () => - requesterV1(`/daily_statistics/occupied_capacity`).then((res: AxiosResponse) => - toCamelcase[]>>(res.data), - ) - -export const fetchStatisticEpochTimeDistribution = () => - requesterV1(`/distribution_data/epoch_time_distribution`).then((res: AxiosResponse) => - toCamelcase>(res.data.data), - ) - -export const fetchStatisticNewNodeCount = () => - requesterV1(`/daily_statistics/nodes_count`).then((res: AxiosResponse) => - toCamelcase[]>>(res.data), - ) - -export const fetchStatisticNodeDistribution = () => - requesterV1(`/distribution_data/nodes_distribution`).then((res: AxiosResponse) => - toCamelcase>(res.data.data), - ) - -export const fetchStatisticTotalSupply = () => - requesterV1(`/daily_statistics/circulating_supply-burnt-locked_capacity`).then((res: AxiosResponse) => - toCamelcase[]>>(res.data), - ) - -export const fetchStatisticAnnualPercentageCompensation = () => - requesterV1(`/monetary_data/nominal_apc`).then((res: AxiosResponse) => - toCamelcase>(res.data.data), - ) - -export const fetchStatisticSecondaryIssuance = () => - requesterV1(`/daily_statistics/treasury_amount-mining_reward-deposit_compensation`).then((res: AxiosResponse) => { - const resp = toCamelcase[]>>(res.data) - return { - ...resp, - data: resp.data.map(wrapper => { - const { depositCompensation, miningReward, treasuryAmount } = wrapper.attributes + fetchStatisticDifficulty: () => + v1GetUnwrappedList(`/daily_statistics/avg_difficulty`).then(items => + // filter latest exceptional data out + items.filter((item, idx) => idx < items.length - 2 || item.avgDifficulty !== '0.0'), + ), + + fetchStatisticHashRate: () => + v1GetUnwrappedList(`/daily_statistics/avg_hash_rate`).then(items => { + return ( + items + // filter latest exceptional data out + .filter((item, idx) => idx < items.length - 2 || item.avgHashRate !== '0.0') + .map(item => ({ + ...item, + avgHashRate: new BigNumber(item.avgHashRate).multipliedBy(1000).toString(), + })) + ) + }), + + fetchStatisticUncleRate: () => + v1GetUnwrappedList(`/daily_statistics/uncle_rate`).then(items => { + return ( + items + // filter latest exceptional data out + .filter((item, idx) => idx < items.length - 2 || item.uncleRate !== '0') + .map(item => ({ + ...item, + uncleRate: new BigNumber(item.uncleRate).toFixed(4), + })) + ) + }), + + fetchStatisticMinerAddressDistribution: () => + v1GetUnwrapped(`/distribution_data/miner_address_distribution`), + + fetchStatisticMinerVersionDistribution: () => + requesterV2(`/blocks/ckb_node_versions`).then((res: AxiosResponse) => + toCamelcase<{ data: Array<{ version: string; blocksCount: number }> }>(res.data), + ), + + fetchStatisticTransactionFees: () => + requesterV2 + .get('statistics/transaction_fees') + .then(res => toCamelcase(res.data)), + + fetchStatisticCellCount: () => + v1GetUnwrappedList>( + `/daily_statistics/live_cells_count-dead_cells_count`, + ).then(items => { + return items.map(item => ({ + ...item, + allCellsCount: (Number(item.liveCellsCount) + Number(item.deadCellsCount)).toString(), + })) + }), + + fetchStatisticDifficultyUncleRateEpoch: () => + v1GetUnwrappedList(`/epoch_statistics/epoch_time-epoch_length`).then( + // Data may enter the cache, so it is purify to reduce volume. + items => items.map(item => pick(item, ['epochNumber', 'epochTime', 'epochLength'])), + ), + + fetchStatisticAddressBalanceRank: () => + v1GetUnwrapped(`/statistics/address_balance_ranking`).then( + res => res.addressBalanceRanking, + ), + + fetchStatisticBalanceDistribution: () => + v1GetUnwrapped(`/distribution_data/address_balance_distribution`), + + fetchStatisticTxFeeHistory: () => v1GetUnwrappedList(`/daily_statistics/total_tx_fee`), + + fetchStatisticBlockTimeDistribution: () => + v1GetUnwrapped(`/distribution_data/block_time_distribution`), + + fetchStatisticAverageBlockTimes: () => + v1GetUnwrapped(`/distribution_data/average_block_time`).then( + res => res.averageBlockTime, + ), + + // Unused currently + fetchStatisticOccupiedCapacity: () => + v1Get[]>(`/daily_statistics/occupied_capacity`), + + fetchStatisticEpochTimeDistribution: () => + v1GetUnwrapped(`/distribution_data/epoch_time_distribution`), + + // Unused currently + fetchStatisticNewNodeCount: () => + v1Get[]>(`/daily_statistics/nodes_count`), + + // Unused currently + fetchStatisticNodeDistribution: () => + v1GetWrapped(`/distribution_data/nodes_distribution`), + + fetchStatisticTotalSupply: () => + v1GetUnwrappedList(`/daily_statistics/circulating_supply-burnt-locked_capacity`), + + fetchStatisticAnnualPercentageCompensation: () => + v1GetUnwrapped(`/monetary_data/nominal_apc`), + + fetchStatisticSecondaryIssuance: () => + v1GetUnwrappedList( + `/daily_statistics/treasury_amount-mining_reward-deposit_compensation`, + ).then(items => { + return items.map(item => { + const { depositCompensation, miningReward, treasuryAmount } = item const sum = Number(treasuryAmount) + Number(miningReward) + Number(depositCompensation) const treasuryAmountPercent = Number(((Number(treasuryAmount) / sum) * 100).toFixed(2)) const miningRewardPercent = Number(((Number(miningReward) / sum) * 100).toFixed(2)) const depositCompensationPercent = (100 - treasuryAmountPercent - miningRewardPercent).toFixed(2) return { - ...wrapper, - attributes: { - ...wrapper.attributes, - treasuryAmount: treasuryAmountPercent.toString(), - miningReward: miningRewardPercent.toString(), - depositCompensation: depositCompensationPercent, - }, + ...item, + treasuryAmount: treasuryAmountPercent.toString(), + miningReward: miningRewardPercent.toString(), + depositCompensation: depositCompensationPercent, } - }), - } - }) + }) + }), -export const fetchStatisticInflationRate = () => - requesterV1(`/monetary_data/nominal_apc50-nominal_inflation_rate-real_inflation_rate`).then((res: AxiosResponse) => - toCamelcase>(res.data.data), - ) + fetchStatisticInflationRate: () => + v1GetUnwrapped( + `/monetary_data/nominal_apc50-nominal_inflation_rate-real_inflation_rate`, + ), -export const fetchStatisticLiquidity = () => - requesterV1(`/daily_statistics/circulating_supply-liquidity`).then((res: AxiosResponse) => { - const resp = toCamelcase[]>>(res.data) - return { - ...resp, - data: resp.data.map(wrapper => ({ - ...wrapper, - attributes: { - ...wrapper.attributes, - daoDeposit: new BigNumber(wrapper.attributes.circulatingSupply) - .minus(new BigNumber(wrapper.attributes.liquidity)) - .toFixed(2), - }, - })), + fetchStatisticLiquidity: () => + v1GetUnwrappedList(`/daily_statistics/circulating_supply-liquidity`).then(items => { + return items.map(item => ({ + ...item, + daoDeposit: new BigNumber(item.circulatingSupply).minus(new BigNumber(item.liquidity)).toFixed(2), + })) + }), + + fetchFlushChartCache: () => v1GetUnwrapped(`statistics/flush_cache_info`), + + fetchSimpleUDT: (typeHash: string) => v1GetUnwrapped(`/udts/${typeHash}`), + + fetchSimpleUDTTransactions: ({ + typeHash, + page, + size, + filter, + type, + }: { + typeHash: string + page: number + size: number + filter?: string | null + type?: string | null + }) => + v1GetUnwrappedPagedList(`/udt_transactions/${typeHash}`, { + params: { + page, + page_size: size, + address_hash: filter?.startsWith('0x') ? undefined : filter, + tx_hash: filter?.startsWith('0x') ? filter : undefined, + transfer_action: type, + }, + }), + + fetchTokens: (page: number, size: number, sort?: string) => + v1GetUnwrappedPagedList(`/udts`, { + params: { + page, + page_size: size, + sort, + }, + }), + + exportTransactions: ({ + type, + id, + date, + block, + }: { + type: State.TransactionCsvExportType + id?: string + date?: Record<'start' | 'end', Dayjs | undefined> + block?: Record<'from' | 'to', number> + }) => { + const rangeParams = { + start_date: date?.start?.valueOf(), + end_date: date?.end?.add(1, 'day').subtract(1, 'millisecond').valueOf(), + start_number: block?.from, + end_number: block?.to, } - }) + if (type === 'nft') { + return requesterV2 + .get(`/nft/transfers/download_csv`, { params: { ...rangeParams, collection_id: id } }) + .then(res => toCamelcase(res.data)) + } + return requesterV1 + .get(`/${type}/download_csv`, { params: { ...rangeParams, id } }) + .then(res => toCamelcase(res.data)) + }, + + fetchNFTCollections: (page: string, sort: string, type?: string) => + requesterV2 + .get<{ + data: Array + pagination: { + count: number + page: number + next: number | null + prev: number | null + last: number + } + }>('nft/collections', { + params: { + page, + sort, + type, + }, + }) + .then(res => res.data), + + fetchNFTCollection: (collection: string) => + requesterV2.get(`nft/collections/${collection}`).then(res => res.data), + + fetchNFTCollectionHolders: (id: string, page: string, sort?: string, addressHash?: string | null) => + requesterV2 + .get<{ + data: Record + }>(`/nft/collections/${id}/holders`, { + params: { + page, + sort, + address_hash: addressHash, + }, + }) + .then(res => res.data), + + fetchNFTCollectionItems: (id: string, page: string) => + requesterV2 + .get<{ + data: Array + pagination: { + count: number + page: number + next: number | null + prev: number | null + last: number + } + }>(`/nft/collections/${id}/items`, { + params: { + page, + }, + }) + .then(res => res.data), + + fetchNFTCollectionItem: (collectionId: string, id: string) => + requesterV2.get(`nft/collections/${collectionId}/items/${id}`).then(res => res.data), + + fetchNFTItemByOwner: (owner: string, standard: string, page?: string) => + requesterV2 + .get<{ + data: Array + pagination: { + series: Array + } + }>('nft/items', { + params: { + owner, + standard, + page, + }, + }) + .then(res => res.data), + + fetchScriptInfo: (codeHash: string, hashType: string) => + requesterV2 + .get('scripts/general_info', { + params: { + code_hash: codeHash, + hash_type: hashType, + }, + }) + .then(res => toCamelcase>(res.data)), + + fetchScriptCKBTransactions: (codeHash: string, hashType: string, page: number, pageSize: number) => + requesterV2 + .get('scripts/ckb_transactions', { + params: { + code_hash: codeHash, + hash_type: hashType, + page, + page_size: pageSize, + }, + }) + .then(res => + toCamelcase< + Response.Response<{ + ckbTransactions: CKBTransactionInScript[] + // TODO: This structure is unexpected and will be adjusted in the future. + // Refs: https://github.com/Magickbase/ckb-explorer-public-issues/issues/451 + meta: { + total: number + pageSize: number + } + }> + >(res.data), + ), + + fetchScriptCells: ( + cellType: 'deployed_cells' | 'referring_cells', + codeHash: string, + hashType: string, + page: number, + pageSize: number, + ) => + requesterV2 + .get(`scripts/${cellType}`, { + params: { + code_hash: codeHash, + hash_type: hashType, + page, + page_size: pageSize, + }, + }) + + .then(res => + toCamelcase< + Response.Response<{ + deployedCells?: CellInScript[] + referringCells?: CellInScript[] + // TODO: This structure is unexpected and will be adjusted in the future. + // Refs: https://github.com/Magickbase/ckb-explorer-public-issues/issues/451 + meta: { + total: number + pageSize: number + } + }> + >(res.data), + ), + + fetchNFTCollectionTransferList: ( + id: string, + page: string, + tokenId?: string | null, + transferAction?: string | null, + addressHash?: string | null, + txHash?: string | null, + ) => + requesterV2 + .get(`/nft/transfers`, { + params: { + page, + collection_id: id, + token_id: tokenId, + transfer_action: transferAction, + address_hash: addressHash, + tx_hash: txHash, + }, + }) + .then(res => res.data), -export const fetchFlushChartCache = () => - requesterV1(`statistics/flush_cache_info`).then((res: AxiosResponse) => - toCamelcase>(res.data.data), - ) - -export const fetchSimpleUDT = (typeHash: string) => - requesterV1(`/udts/${typeHash}`).then((res: AxiosResponse) => toCamelcase>(res.data.data)) - -export const fetchSimpleUDTTransactions = ({ - typeHash, - page, - size, - filter, - type, -}: { - typeHash: string - page: number - size: number - filter?: string | null - type?: string | null -}) => - requesterV1(`/udt_transactions/${typeHash}`, { - params: { - page, - page_size: size, - address_hash: filter?.startsWith('0x') ? undefined : filter, - tx_hash: filter?.startsWith('0x') ? filter : undefined, - transfer_action: type, - }, - }).then((res: AxiosResponse) => toCamelcase[]>>(res.data)) - -export const fetchTokens = (page: number, size: number, sort?: string) => - requesterV1(`/udts`, { - params: { - page, - page_size: size, - sort, - }, - }).then((res: AxiosResponse) => toCamelcase[]>>(res.data)) - -export const exportTransactions = ({ - type, - id, - date, - block, -}: { - type: State.TransactionCsvExportType - id?: string - date?: Record<'start' | 'end', Dayjs | undefined> - block?: Record<'from' | 'to', number> -}) => { - const rangeParams = { - start_date: date?.start?.valueOf(), - end_date: date?.end?.add(1, 'day').subtract(1, 'millisecond').valueOf(), - start_number: block?.from, - end_number: block?.to, + fetchDASAccounts: async (addresses: string[]): Promise => { + const { data } = await requesterV2.post>('das_accounts', { + addresses, + }) + const dataWithNormalizeEmptyValue = Object.fromEntries( + Object.entries(data).map(([addr, account]) => { + return account === '' ? [addr, null] : [addr, account] + }), + ) + return dataWithNormalizeEmptyValue + }, +} + +// ==================== +// Types +// ==================== + +export namespace FeeRateTracker { + export interface TransactionFeeRate { + id: number + timestamp: number + feeRate: number + confirmationTime: number } - if (type === 'nft') { - return requesterV2 - .get(`/nft/transfers/download_csv`, { params: { ...rangeParams, collection_id: id } }) - .then(res => toCamelcase(res.data)) + + export interface PendingTransactionFeeRate { + id: number + feeRate: number + } + + export interface LastNDaysTransactionFeeRate { + date: string + feeRate: string + } + + export interface TransactionFeesStatistic { + transactionFeeRates: TransactionFeeRate[] + pendingTransactionFeeRates: PendingTransactionFeeRate[] + lastNDaysTransactionFeeRates: LastNDaysTransactionFeeRate[] + } + + export interface FeeRateCard { + priority: string + icon: ReactNode + feeRate?: string + priorityClass: string + confirmationTime: number + } +} + +export interface NFTCollection { + id: number + standard: string + name: string + description: string + h24_ckb_transactions_count: string + creator: string | null + icon_url: string | null + items_count: number | null + holders_count: number | null + type_script: { code_hash: string; hash_type: 'data' | 'type'; args: string } | null +} + +export interface NFTItem { + icon_url: string | null + id: number + token_id: string + owner: string | null + standard: string | null + cell: { + cell_index: number + data: string | null + status: string + tx_hash: string + } | null + collection: NFTCollection + name: string | null + metadata_url: string | null +} + +export interface ScriptInfo { + id: string + scriptName: string + scriptType: string + codeHash: string + hashType: 'type' | 'data' + capacityOfDeployedCells: string + capacityOfReferringCells: string + countOfTransactions: number + countOfDeployedCells: number + countOfReferringCells: number +} + +export interface CKBTransactionInScript { + id: number + txHash: string + blockId: number + blockNumber: number + blockTimestamp: number + transactionFee: number + isCellbase: boolean + txStatus: string + displayInputs: State.Cell[] + displayOutputs: State.Cell[] +} + +export interface CellInScript { + id: number + capacity: string + data: string + ckbTransactionId: number + createdAt: string + updatedAt: string + status: string + addressId: number + blockId: number + txHash: string + cellIndex: number + generatedById?: number + consumedById?: number + cellType: string + dataSize: number + occupiedCapacity: number + blockTimestamp: number + consumedBlockTimestamp: number + typeHash?: string + udtAmount: number + dao: string + lockScriptId?: number + typeScriptId?: number +} + +export interface TransferRes { + id: number + from: string | null + to: string | null + action: 'mint' | 'normal' | 'destruction' + item: NFTItem + transaction: { + tx_hash: string + block_number: number + block_timestamp: number + } +} + +export interface TransferListRes { + data: Array + pagination: { + count: number + page: number + next: number | null + prev: number | null + last: number } - return requesterV1 - .get(`/${type}/download_csv`, { params: { ...rangeParams, id } }) - .then(res => toCamelcase(res.data)) } + +export type DASAccount = string + +export type DASAccountMap = Record diff --git a/src/services/ExplorerService/index.ts b/src/services/ExplorerService/index.ts index 349f49ea5..f30ef6837 100644 --- a/src/services/ExplorerService/index.ts +++ b/src/services/ExplorerService/index.ts @@ -1,6 +1,6 @@ import { BehaviorSubject, Subscription, map, switchMap, timer } from 'rxjs' import { BLOCK_POLLING_TIME } from '../../constants/common' -import * as apiFetcher from './fetcher' +import { apiFetcher } from './fetcher' const initStatistics: State.Statistics = { tipBlockNumber: '0', @@ -41,19 +41,14 @@ class ExplorerService { start() { this.callbacksAtStop = new Subscription() this.callbacksAtStop.add( - timer(0, BLOCK_POLLING_TIME) - .pipe( - switchMap(this.api.fetchStatistics), - map(wrapper => wrapper.attributes), - ) - .subscribe(this.latestStatistics$), + timer(0, BLOCK_POLLING_TIME).pipe(switchMap(this.api.fetchStatistics)).subscribe(this.latestStatistics$), ) this.callbacksAtStop.add( timer(0, BLOCK_POLLING_TIME) .pipe( switchMap(this.api.fetchTipBlockNumber), - map(wrapper => Number(wrapper.attributes.tipBlockNumber)), + map(tipBlockNumber => Number(tipBlockNumber)), ) .subscribe(this.latestBlockNumber$), ) diff --git a/src/services/ExplorerService/types.ts b/src/services/ExplorerService/types.ts index 1de3e208d..53bafacb1 100644 --- a/src/services/ExplorerService/types.ts +++ b/src/services/ExplorerService/types.ts @@ -19,9 +19,9 @@ export namespace Response { pageSize: number } - export interface Wrapper { + export interface Wrapper { id: number - type: string - attributes: T + type: T + attributes: A } } diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 652548922..637421049 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -5,28 +5,6 @@ declare namespace State { hashType: string } - export interface Data { - data: string - } - - export interface NodeVersion { - version: string - } - - export interface ToastMessage { - message: string - type: 'success' | 'warning' | 'danger' - duration?: number - id: number - } - - export interface AppError { - type: 'Network' | 'ChainAlert' | 'Maintenance' - message: string[] - } - - type CellTypes = Cell['cellType'] - interface UDTInfo { symbol: string amount: string @@ -340,22 +318,6 @@ declare namespace State { createTimestamp?: number } - export interface BlockchainInfo { - blockchainInfo: { - isInitialBlockDownload: boolean - epoch: string - difficulty: string - medianTime: string - chain: string - alerts: { - id: string - message: string - noticeNntil: string - priority: string - }[] - } - } - export interface NervosDao { totalDeposit: string depositorsCount: string @@ -623,14 +585,6 @@ declare namespace State { uan?: string } - export interface AddressState { - address: Address - transactions: Transaction[] - total: number - addressStatus: FetchStatus - transactionsStatus: FetchStatus - } - export type TransactionCsvExportType = 'address_transactions' | 'blocks' | 'udts' | 'nft' export interface ChartColor { @@ -643,7 +597,5 @@ declare namespace State { liquidityColors: string[] } - type SortOrderTypes = 'asc' | 'desc' - type Theme = { primary: string } } diff --git a/src/utils/chart.ts b/src/utils/chart.ts index b3c451fae..908911aa8 100644 --- a/src/utils/chart.ts +++ b/src/utils/chart.ts @@ -1,6 +1,7 @@ import BigNumber from 'bignumber.js' import { EChartOption } from 'echarts' import { SeriesItem } from '../pages/StatisticsChart/common' +import type { FeeRateTracker } from '../services/ExplorerService/fetcher' export const DATA_ZOOM_CONFIG = [ { diff --git a/src/utils/hook.ts b/src/utils/hook.ts index 38a6b9b4b..3cfc81778 100644 --- a/src/utils/hook.ts +++ b/src/utils/hook.ts @@ -182,12 +182,14 @@ export function useSearchParams(...names: T[]): Partial getSearchParams(location.search, names), [location.search, names]) } +export type OrderByType = 'asc' | 'desc' + // REFACTOR: remove useSearchParams export function useSortParam( isSortBy: (s?: string) => boolean, ): { sortBy: T | undefined - orderBy: State.SortOrderTypes + orderBy: OrderByType sort?: string handleSortClick: (sortRule?: T) => void } { @@ -195,13 +197,13 @@ export function useSortParam( function isSortByType(s?: string): s is SortType { return isSortBy(s) || s === undefined } - function isOrderByType(s?: string): s is State.SortOrderTypes { + function isOrderByType(s?: string): s is OrderByType { return s === 'asc' || s === 'desc' } const { sort: sortParam } = useSearchParams('sort') const updateSearchParams = useUpdateSearchParams<'sort' | 'page'>() let sortBy: SortType - let orderBy: State.SortOrderTypes = 'asc' + let orderBy: OrderByType = 'asc' if (sortParam) { const sortEntry = sortParam.split(',')[0] const indexOfPoint = sortEntry.indexOf('.') diff --git a/src/utils/transformer.ts b/src/utils/transformer.ts index e5c1789a0..a9305b2f4 100644 --- a/src/utils/transformer.ts +++ b/src/utils/transformer.ts @@ -1,6 +1,6 @@ -import { CellInScript, CkbTransactionInScript } from '../pages/Script/types' +import type { CellInScript, CKBTransactionInScript } from '../services/ExplorerService/fetcher' -export const transformToTransaction = (tx: CkbTransactionInScript): State.Transaction => { +export const transformToTransaction = (tx: CKBTransactionInScript): State.Transaction => { return { transactionHash: tx.txHash, blockNumber: tx.blockNumber, diff --git a/src/utils/util.ts b/src/utils/util.ts index 1e7ceba87..03ac917ee 100644 --- a/src/utils/util.ts +++ b/src/utils/util.ts @@ -332,11 +332,11 @@ export function randomInt(min: number, max: number) { return min + Math.floor(Math.random() * (max - min + 1)) } -export const isDaoDepositCell = (cellType: State.CellTypes) => cellType === 'nervos_dao_deposit' +export const isDaoDepositCell = (cellType: State.Cell['cellType']) => cellType === 'nervos_dao_deposit' -export const isDaoWithdrawCell = (cellType: State.CellTypes) => cellType === 'nervos_dao_withdrawing' +export const isDaoWithdrawCell = (cellType: State.Cell['cellType']) => cellType === 'nervos_dao_withdrawing' -export const isDaoCell = (cellType: State.CellTypes) => isDaoDepositCell(cellType) || isDaoWithdrawCell(cellType) +export const isDaoCell = (cellType: State.Cell['cellType']) => isDaoDepositCell(cellType) || isDaoWithdrawCell(cellType) export default { copyElementValue, From 2c7fd6afed4778ed5159d7ba1cd8a660213f7950 Mon Sep 17 00:00:00 2001 From: WhiteMind Date: Wed, 25 Oct 2023 17:35:29 +0800 Subject: [PATCH 2/3] Refactor: Remove the index.d.ts file and move its types to the appropriate location (#124) * refactor: move the Cell and Script types to src/models * refactor: move the Block, CellDep, and Transaction types to src/models * refactor: move the Address and UDT types to src/models * refactor: remove unnecessary mocks files * refactor: move the process of handling the data structure of the chart API response to the fetcher * refactor: extract the Chart-related types from index.d.ts to a more appropriate location * refactor: extract some types from index.d.ts to a more appropriate location * refactor: move State.ChartColor to a more appropriate location * refactor: use [] instead Array --- .eslintrc.js | 1 + src/App.tsx | 4 +- src/__mocks__/block.ts | 149 ----- src/__mocks__/charts.ts | 272 -------- src/__mocks__/home.ts | 75 --- src/components/CsvExport/index.tsx | 3 +- .../Header/BlockchainComp/styled.tsx | 5 +- src/components/Header/LanguageComp/styled.tsx | 5 +- .../NftCollectionInventory/index.tsx | 2 +- src/components/NftHolderList/index.tsx | 2 +- src/components/Script/index.tsx | 5 +- src/components/Table/styled.tsx | 2 +- src/components/Transaction/Cellbase/index.tsx | 3 +- .../TransactionCellArrow/index.tsx | 7 +- .../TransactionIncome/styled.tsx | 4 +- .../TransactionItemCell/index.tsx | 13 +- .../TransactionItemCell/styled.tsx | 15 +- .../TransactionItemCellList/index.tsx | 8 +- .../TransactionLiteItem/index.tsx | 3 +- src/components/TransactionItem/index.tsx | 3 +- src/constants/common.ts | 1 + src/constants/scripts.ts | 4 +- src/models/Address/UDTAccount.ts | 67 ++ src/models/Address/index.ts | 31 + src/models/Block/index.ts | 33 + src/models/Cell/index.ts | 112 ++++ src/models/Script/index.ts | 5 + src/models/Transaction/index.ts | 36 ++ src/models/UDT/index.ts | 19 + src/pages/Address/AddressComp.tsx | 22 +- src/pages/Address/state.ts | 4 +- src/pages/BlockDetail/BlockComp.tsx | 6 +- src/pages/BlockDetail/state.ts | 4 +- src/pages/BlockList/index.tsx | 9 +- src/pages/ExportTransactions/index.tsx | 4 +- .../FeeRateTracker/FeeRateTrackerComp.tsx | 2 +- .../Home/AverageBlockTimeChart/index.tsx | 7 +- src/pages/Home/HashRateChart/index.tsx | 4 +- src/pages/Home/TableCard/index.tsx | 6 +- src/pages/Home/index.tsx | 13 +- src/pages/NervosDao/DaoOverview/index.tsx | 13 +- src/pages/NervosDao/DaoOverview/styled.tsx | 14 +- src/pages/NervosDao/DaoTransactions/index.tsx | 5 +- src/pages/NervosDao/DepositorRank/index.tsx | 11 +- src/pages/NervosDao/state.ts | 4 +- src/pages/NftCollectionInfo/index.tsx | 2 +- src/pages/NftCollections/List.tsx | 6 +- src/pages/ScriptList/index.tsx | 2 +- src/pages/SimpleUDT/SimpleUDTComp.tsx | 10 +- src/pages/SimpleUDT/index.tsx | 3 +- src/pages/SimpleUDT/state.ts | 4 +- .../activities/AddressBalanceRank.tsx | 17 +- .../activities/AddressCount.tsx | 9 +- .../activities/BalanceDistribution.tsx | 24 +- .../StatisticsChart/activities/CellCount.tsx | 9 +- .../activities/TransactionCount.tsx | 9 +- .../activities/TxFeeHistory.tsx | 9 +- .../block/AverageBlockTime.tsx | 11 +- .../block/BlockTimeDistribution.tsx | 33 +- .../block/EpochTimeDistribution.tsx | 23 +- src/pages/StatisticsChart/common/index.tsx | 4 +- .../StatisticsChart/mining/Difficulty.tsx | 9 +- .../mining/DifficultyHashRate.tsx | 9 +- .../mining/DifficultyUncleRateEpoch.tsx | 9 +- src/pages/StatisticsChart/mining/HashRate.tsx | 9 +- .../mining/MinerAddressDistribution.tsx | 25 +- .../mining/MinerVersionDistribution.tsx | 7 +- .../StatisticsChart/mining/UncleRate.tsx | 11 +- .../monetary/AnnualPercentageCompensation.tsx | 22 +- .../monetary/InflationRate.tsx | 28 +- .../StatisticsChart/monetary/Liquidity.tsx | 9 +- .../monetary/SecondaryIssuance.tsx | 9 +- .../StatisticsChart/monetary/TotalSupply.tsx | 9 +- .../nervosDao/CirculationRatio.tsx | 9 +- .../nervosDao/NewDaoDeposit.tsx | 9 +- .../nervosDao/TotalDaoDeposit.tsx | 9 +- src/pages/Tokens/index.tsx | 3 +- .../Transaction/TransactionCell/index.tsx | 13 +- .../Transaction/TransactionCell/styled.tsx | 7 +- .../Transaction/TransactionCellList/index.tsx | 5 +- .../TransactionCellScript/index.tsx | 5 +- .../TransactionCellScript/styled.tsx | 9 +- .../TransactionComp/TransactionComp.tsx | 6 +- .../TransactionLite/TransactionBadge.tsx | 8 +- .../TransactionLite/TransactionLite.tsx | 12 +- .../TransactionComp/TransactionOverview.tsx | 5 +- .../Transaction/TransactionReward/index.tsx | 5 +- src/pages/Transaction/state.ts | 7 +- src/pages/TransactionList/index.tsx | 21 +- src/routes/state.ts | 7 +- src/services/ExplorerService/fetcher.ts | 264 ++++++-- src/services/ExplorerService/index.ts | 7 +- src/services/ExplorerService/types.ts | 184 ++++++ src/types/index.d.ts | 601 ------------------ src/types/styled.d.ts | 8 + src/utils/chart.ts | 4 +- src/utils/transformer.ts | 8 +- src/utils/util.ts | 12 +- 98 files changed, 1047 insertions(+), 1529 deletions(-) delete mode 100644 src/__mocks__/block.ts delete mode 100644 src/__mocks__/charts.ts delete mode 100644 src/__mocks__/home.ts create mode 100644 src/models/Address/UDTAccount.ts create mode 100644 src/models/Address/index.ts create mode 100644 src/models/Block/index.ts create mode 100644 src/models/Cell/index.ts create mode 100644 src/models/Script/index.ts create mode 100644 src/models/Transaction/index.ts create mode 100644 src/models/UDT/index.ts delete mode 100644 src/types/index.d.ts create mode 100644 src/types/styled.d.ts diff --git a/.eslintrc.js b/.eslintrc.js index e88c2f0ee..447fd234e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -99,6 +99,7 @@ module.exports = { allow: ['^.*_'], }, ], + '@typescript-eslint/array-type': 'error', }, env: { jest: true, diff --git a/src/App.tsx b/src/App.tsx index aacfb16b7..aa0ff34fa 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,6 @@ import { useMemo } from 'react' import { QueryClient, QueryClientProvider } from 'react-query' -import { ThemeProvider } from 'styled-components' +import { DefaultTheme, ThemeProvider } from 'styled-components' import Routers from './routes' import Toast from './components/Toast' import useInitApp from './contexts/providers/hook' @@ -18,7 +18,7 @@ const queryClient = new QueryClient() const App = () => { useInitApp() - const theme = useMemo( + const theme = useMemo( () => ({ primary: getPrimaryColor(), secondary: getSecondaryColor(), diff --git a/src/__mocks__/block.ts b/src/__mocks__/block.ts deleted file mode 100644 index 614c23be2..000000000 --- a/src/__mocks__/block.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { toCamelcase } from '../utils/util' - -export const blockMock = () => { - return toCamelcase<{ - block: State.Block - transactions: State.Transaction[] - total: number - status: State.FetchStatus - }>({ - block: { - block_hash: '0xd48737284040b2c6d221a16ba65b1497593ffb7b9cd250a1d0f40aecc5c8d4c0', - block_index_in_epoch: '209', - cell_consumed: '6100000000', - difficulty: '72969622108522274', - epoch: '1038', - length: '1800', - miner_hash: 'ckb1qyqgsqsd549m333prhzyvdvedluuuas9u2ssxkmf53', - miner_reward: '106544901066', - nonce: '218174220386130088675393873778844041472', - number: '1706701', - proposals_count: '0', - received_tx_fee: '0', - received_tx_fee_status: 'pending', - reward: '106544901066', - reward_status: 'pending', - start_number: '1706492', - timestamp: '1589002865754', - total_cell_capacity: '111298326112', - total_transaction_fee: '0', - transactions_count: '1', - transactions_root: '0xefa2d4a0c3c673ddcbf165a217bb8eb5c01765faaf51702a58b45184816a79a6', - uncle_block_hashes: null, - uncles_count: '0', - version: '0', - }, - transactions: { - data: [ - { - is_cellbase: true, - witnesses: [ - '0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce80114000000dde7801c073dfb3464c7b1f05b806bb2bbb84e990400000000000000', - ], - cell_deps: [], - header_deps: null, - transaction_hash: '0xa8651bdc77bb196443ffd89ec8802ad6725bd36ba43a159cf15951e2102b2a65', - transaction_fee: '0', - block_number: '1706099', - version: '0', - block_timestamp: '1588997260542', - display_inputs: [ - { - id: '', - from_cellbase: true, - capacity: '', - address_hash: '', - target_block_number: '1706088', - generated_tx_hash: '0xa8651bdc77bb196443ffd89ec8802ad6725bd36ba43a159cf15951e2102b2a65', - }, - ], - display_outputs: [ - { - id: '2275538', - capacity: '139900315691.0', - address_hash: 'ckb1qyqquqf8c6002yzr72qvz3kxfa7zfnrsm4sqflv9zd', - target_block_number: '1706088', - base_reward: '133925154970', - commit_reward: '0', - proposal_reward: '0', - secondary_reward: '5975160721', - status: 'live', - consumed_tx_hash: '', - }, - ], - }, - { - is_cellbase: false, - witnesses: [ - '0x55000000100000005500000055000000410000008d0c7c8a9a433018911f24c47a0a561c9778dc0c9766767158f33c02561165640c80e6978657e312823704f028866e65a339bdc062e63cf75ecb30d3b8f4e8db01', - '0x', - ], - cell_deps: [ - { - dep_type: 'dep_group', - out_point: { - index: 0, - tx_hash: '0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c', - }, - }, - { - dep_type: 'code', - out_point: { - index: 2, - tx_hash: '0xe2fb199810d49a4d8beec56718ba2593b665db9d52299a0f9e6e75416d73ff5c', - }, - }, - ], - header_deps: null, - transaction_hash: '0x79238be9c68d888b976cdc86e40a0ca03f223d7487033cc0755283b79376ec66', - transaction_fee: '3684', - block_number: '1706099', - version: '0', - block_timestamp: '1588997260542', - display_inputs: [ - { - id: '2115183', - from_cellbase: false, - capacity: '930300000000.0', - address_hash: 'ckb1qyqz67uumxel602l5t6zqf7qhqt6untg9z9qukhlha', - generated_tx_hash: '0x5f0cec7e77478158a60242f02768c94db075de90b93735dd413241e3ac9dfd21', - cell_index: '0', - cell_type: 'normal', - }, - { - id: '2114922', - from_cellbase: false, - capacity: '9971320520000.0', - address_hash: 'ckb1qyqz67uumxel602l5t6zqf7qhqt6untg9z9qukhlha', - generated_tx_hash: '0x44c67dc4b5648120841c6cb73d8b13c2b2d7474df500b87a2241c550b06179ea', - cell_index: '0', - cell_type: 'normal', - }, - ], - display_outputs: [ - { - id: '2275539', - capacity: '10800000000000.0', - address_hash: 'ckb1qyqzwn57q5p2a0mxv6s02zvs28ny3rjxh38qc2j3zn', - status: 'live', - consumed_tx_hash: '', - cell_type: 'nervos_dao_deposit', - }, - { - id: '2275540', - capacity: '101620516316.0', - address_hash: 'ckb1qyqyg3ah0spf7ggsanjduvexl9psfp0fdypsa7jfwf', - status: 'live', - consumed_tx_hash: '', - cell_type: 'normal', - }, - ], - }, - ], - }, - total: 2, - status: 'OK', - }) -} - -export default blockMock diff --git a/src/__mocks__/charts.ts b/src/__mocks__/charts.ts deleted file mode 100644 index f8bfe594d..000000000 --- a/src/__mocks__/charts.ts +++ /dev/null @@ -1,272 +0,0 @@ -export const AddressBalanceRankMock = [ - { - ranking: '1', - address: 'ckb1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqchfq7c4e0e864p98x0t7mc0k58thz83s97znnd', - balance: '840000000000000000', - }, - { - ranking: '2', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32sdufwedw7a0w9dkvhpsah4mdk2gkfq63e0q6qzzqxzq8yqnqq85p', - balance: '285600000000000000', - }, - { - ranking: '3', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32sn23uga5m8u5v87q98vr29qa8tl0ruu84gqfqzzqxzq8yqc2dxk6', - balance: '142800000000000000', - }, - { - ranking: '4', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32s3y29vjv73cfm8qax220dwwmpdccl4upy4s9qzzqxzq8yqyd09am', - balance: '126000000000000000', - }, - { - ranking: '5', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32338kguc9ftfvtsw7tsm89f6zsujdtjpw2pq4qzzqxzq8yqvxaz77', - balance: '84000000000000000', - }, - { - ranking: '6', - address: 'ckb1qyq8z93tpxwj9ugm7yxafqtlyljeycum4zks4vzxjq', - balance: '67200000000000000', - }, - { - ranking: '7', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32s8ulyn4d0l8ztr8fzy4hzjmnn0uufyknj4s9qzzqxzq8yqd5ry6k', - balance: '67073503700000000', - }, - { - ranking: '8', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32se9x2xulmzuugf2ndr4v6mljxjvl4x4twpq4qzzqxzq8yqu5luw5', - balance: '56011200000000000', - }, - { - ranking: '9', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32s3g55rl3hg5hfy3kcde0q6vvp4m742tkqasvqzzqxzq8yqxfg705', - balance: '42000000000000000', - }, - { - ranking: '10', - address: 'ckb1qyqxpyph6y7t3qfv8sq4kjkyh4dkqns52ngqdfh6vu', - balance: '40320864200000000', - }, - { - ranking: '11', - address: 'ckb1qyqql4j6ulr3pnhfkqy2y0sp692s48n2pwgsgzw2s9', - balance: '36400000000000000', - }, - { - ranking: '12', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn3239p2wh7cvwsyn3jexs2y39d9d7u9wq3fsasvqzzqxzq8yq4eh30x', - balance: '27988800000000000', - }, - { - ranking: '13', - address: 'ckb1qyqyj92w7wunlq0pczmmgrjddkp36pcum2vsmukrfv', - balance: '24549553800000000', - }, - { - ranking: '14', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32spqzxnw7xlqh8ktjyryv8932ll2588d8ypq4qzzqxzq8yqk3m6dz', - balance: '24164832000000000', - }, - { - ranking: '15', - address: 'ckb1qyqxn80cz3eehramv0zt26h35p9ucn7pqq7q79dkmx', - balance: '22400010000000000', - }, - { - ranking: '16', - address: 'ckb1qyqyrryqd8j79g64h2xzhsqwt2l2dpjpsp2sqklpcl', - balance: '22400000000000000', - }, - { - ranking: '17', - address: 'ckb1qyqp7zqdqut4554fym8vnrpxw5ju3nfcuumqwvxgp2', - balance: '22132859128410000', - }, - { - ranking: '18', - address: 'ckb1qyq8k2zw044q88rm48wklnmw32y6rkdjwu6qq347h0', - balance: '22099999999998608', - }, - { - ranking: '19', - address: 'ckb1qyq8cakcjf490h7vj9gegzhj8pur7hsguz3qlu2e5u', - balance: '20300743058852006', - }, - { - ranking: '20', - address: 'ckb1qyqpv8lwwnme2qwx9y8qjycpdqjjglgdg9qszs4p7k', - balance: '18288760300000000', - }, - { - ranking: '21', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn3230u4dyhg2h867ppde95279s6g3tmrp6kf6qrqzzqxzq8yqukyhwm', - balance: '18200000000000000', - }, - { - ranking: '22', - address: 'ckb1qyqwuyg8jjdct8skueufvzt5zszuteqkc63q4vmp2r', - balance: '17460009137242396', - }, - { - ranking: '23', - address: 'ckb1qyqt65zcqxammc32eq3t57kx84r2lhvyjx9s06r4s7', - balance: '16723490800000000', - }, - { - ranking: '24', - address: 'ckb1qyqqhenp0fetvv4a585ptekfcuyrsq384g8s5tp8d9', - balance: '15598849369991648', - }, - { - ranking: '25', - address: 'ckb1qyqqqe8h6kyw7zlls39wnfhppdarw858dx3smxlvwe', - balance: '14001508757900638', - }, - { - ranking: '26', - address: 'ckb1qyqqad3nkgfsju8a94rufwgnjt42skp9qwmsuva9ur', - balance: '13313493791002283', - }, - { - ranking: '27', - address: 'ckb1qyqvmvfcffghysdaqh27szr6lgvtwm7aalvsxfmam7', - balance: '13229662800000000', - }, - { - ranking: '28', - address: 'ckb1qyqgvz8gftk7895d986ua7k74vzrky00qlrqc9tfe7', - balance: '13093820200000000', - }, - { - ranking: '29', - address: 'ckb1qyqrsrv2avml5n8hjj77a6tmx20374qdvaqq2ffsgt', - balance: '12579109589000000', - }, - { - ranking: '30', - address: 'ckb1qyqrhkqe5qgf2kwe0zwr8htaeurc0tq85q8q26m6tp', - balance: '12499900000000000', - }, - { - ranking: '31', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32styjm07sggt3ftdmvwnclvuz4qqulqhrzasvqzzqxzq8yq4ylxra', - balance: '12082416000000000', - }, - { - ranking: '32', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32s7pkxekjernj5a3ng5n9dz8fec4zwdppf6qrqzzqxzq8yqh0q8cm', - balance: '12075168000000000', - }, - { - ranking: '33', - address: 'ckb1qyq08nz88qryjhqwj4frxynx2xflllf0v6fs0qugdt', - balance: '11729943334400000', - }, - { - ranking: '34', - address: 'ckb1qyqgwl6f3x52ql4rx6cwt2wcjpnskxzn6acqlem5yp', - balance: '11432337699749000', - }, - { - ranking: '35', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32s04es0ugg4ww2tafkv825lqkarj086kf86qrqzzqxzq8yqafkk7a', - balance: '11200000000000000', - }, - { - ranking: '36', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn3232p7r2yh0v9dx574nuurnc4ql5aya8xjp6qrqzzqxzq8yqym65et', - balance: '11200000000000000', - }, - { - ranking: '37', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32s334728lt9kxmzp65j409ralmrgygqrmr6qrqzzqxzq8yq06a2y5', - balance: '11200000000000000', - }, - { - ranking: '38', - address: 'ckb1qyqq6t46vqpvj07reu3guad526724rucj7gqyt6dj2', - balance: '11199999999997726', - }, - { - ranking: '39', - address: 'ckb1qyqwlpr6nas40kh46h4hs03k8ezfu229q3mqg7wjwe', - balance: '11143874332365963', - }, - { - ranking: '40', - address: 'ckb1qyqtkxxnls3x78pnww3rxdhx4g8hwjswrl6san8whm', - balance: '11000000000000000', - }, - { - ranking: '41', - address: 'ckb1qyqt5q7myl336x0tcn76266yp7ujxyxkf58q2eh2uh', - balance: '10866300000000000', - }, - { - ranking: '42', - address: 'ckb1qyqy6mtud5sgctjwgg6gydd0ea05mr339lnslczzrc', - balance: '10300077100000000', - }, - { - ranking: '43', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32350ke8rkurg45apculyaqmf2qjcaca0zjpq4qzzqxzq8yqj0k9a7', - balance: '10082016000000000', - }, - { - ranking: '44', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32sdqdj9ky0n6ak8tramtuhm79ughp22r5cpq4qzzqxzq8yqdsmszf', - balance: '10082016000000000', - }, - { - ranking: '45', - address: 'ckb1qyqxaewt225xag52654r8ter4424l86mkvfsvvetsz', - balance: '10068971700000000', - }, - { - ranking: '46', - address: 'ckb1qyq9uxwmunv0sxrtne4zedsnv69zra5krpjqqywc9w', - balance: '10000000000000000', - }, - { - ranking: '47', - address: - 'ckb1q3w9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn323kte7wntr3f4jtsu4dz6wvd5u6e8wxxlypq4qzzqxzq8yq30xlae', - balance: '10000000000000000', - }, - { - ranking: '48', - address: 'ckb1qyq86wla89n4ymnltvwdv63mkltj9n4tytqssewevx', - balance: '9658745000000000', - }, - { - ranking: '49', - address: 'ckb1qyqzpjrn7qtwhe8fmnx5p98uyk45wlqadqdq273g50', - balance: '9208675909864514', - }, - { - ranking: '50', - address: 'ckb1qyqdfh83q8t83el8xc48pxlle42yz33tl7tqkh3n2a', - balance: '9184554258000000', - }, -] - -export default AddressBalanceRankMock diff --git a/src/__mocks__/home.ts b/src/__mocks__/home.ts deleted file mode 100644 index fd6812867..000000000 --- a/src/__mocks__/home.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { toCamelcase } from '../utils/util' - -export const latestBlocksMock = () => { - return toCamelcase([ - { - miner_hash: 'ckb1qyqqzmlaljcrd0mneh63sx4gzlyvhz0g35nsh6e30l', - number: '1686123', - timestamp: '1588834270583', - reward: '111302205934', - transactions_count: '1', - live_cell_changes: '1', - }, - { - miner_hash: 'ckb1qyqdmeuqrsrnm7e5vnrmruzmsp4m9wacf6vsxasryq', - number: '1686122', - timestamp: '1588834268229', - reward: '111302206061', - transactions_count: '1', - live_cell_changes: '1', - }, - { - miner_hash: 'ckb1qyqdmeuqrsrnm7e5vnrmruzmsp4m9wacf6vsxasryq', - number: '1686121', - timestamp: '1588834265890', - reward: '111302206188', - transactions_count: '1', - live_cell_changes: '1', - }, - ]) as State.Block[] -} - -export const latestTxMock = () => { - return toCamelcase([ - { - transaction_hash: '0x896ff246313e0a9fc7b392af6665774460d6d8614783f40764b17864bde10643', - block_number: '1687517', - block_timestamp: '1588845263114', - capacity_involved: '111303491978', - live_cell_changes: '1', - }, - { - transaction_hash: '0x19ec38d35659da5a9e5d941ee269a134002fa43344bf83e65ea4b2cbbc40a2c2', - block_number: '1687509', - block_timestamp: '1588845185757', - capacity_involved: '111303525492', - live_cell_changes: '1', - }, - { - transaction_hash: '0x69df00a2b94ab0372a4ecd4ce8ac7dd44c15b406ed5154d5425f9bfb3119a8d0', - block_number: '1687501', - block_timestamp: '1588845110452', - capacity_involved: '111303562450', - live_cell_changes: '1', - }, - ]) as State.Transaction[] -} - -export const statisticMock = (): State.Statistics => { - return toCamelcase({ - epoch_info: { - epoch_number: '1026', - epoch_length: '1800', - index: '1402', - }, - tip_block_number: '1687044', - average_block_time: '8017.05', - current_epoch_difficulty: '62246971368478813', - hash_rate: '7722354397338.191716364963207719284020190144128439', - estimated_epoch_time: '14509117.647058823529411764705882352941176470588234368272300616634345632', - transactions_last_24hrs: '11302', - transactions_count_per_minute: '7.783411604018934645536699908', - }) as State.Statistics -} - -export const TipBlockNumberMock = 1686163 diff --git a/src/components/CsvExport/index.tsx b/src/components/CsvExport/index.tsx index ba4ebd341..33fe91452 100644 --- a/src/components/CsvExport/index.tsx +++ b/src/components/CsvExport/index.tsx @@ -2,8 +2,9 @@ import { Link } from 'react-router-dom' import { useTranslation } from 'react-i18next' import styles from './styles.module.scss' import { ReactComponent as ExportIcon } from '../../assets/export_icon.svg' +import { SupportedExportTransactionType } from '../../services/ExplorerService' -export function CsvExport({ type, id }: { type: State.TransactionCsvExportType; id?: string }) { +export function CsvExport({ type, id }: { type: SupportedExportTransactionType; id?: string }) { const [t] = useTranslation() return ( ` display: flex; flex-direction: column; align-items: flex-start; @@ -69,8 +69,7 @@ export const MobileSubMenuPanel = styled.div` } .mobileMenusMainItemContent { - color: ${(props: { showSubMenu: boolean; theme: State.Theme }) => - props.showSubMenu ? props.theme.primary : 'white'}; + color: ${props => (props.showSubMenu ? props.theme.primary : 'white')}; } .mobileMenusMainItemContentHighlight { diff --git a/src/components/Header/LanguageComp/styled.tsx b/src/components/Header/LanguageComp/styled.tsx index 6ba02f502..a81eebe99 100644 --- a/src/components/Header/LanguageComp/styled.tsx +++ b/src/components/Header/LanguageComp/styled.tsx @@ -35,7 +35,7 @@ export const HeaderLanguagePanel = styled.div` } ` -export const MobileSubMenuPanel = styled.div` +export const MobileSubMenuPanel = styled.div<{ showSubMenu: boolean }>` display: flex; flex-direction: column; align-items: flex-start; @@ -49,8 +49,7 @@ export const MobileSubMenuPanel = styled.div` } .mobileMenusMainItemContent { - color: ${(props: { showSubMenu: boolean; theme: State.Theme }) => - props.showSubMenu ? props.theme.primary : 'white'}; + color: ${props => (props.showSubMenu ? props.theme.primary : 'white')}; } .mobileMenusMainItemContentHighlight { diff --git a/src/components/NftCollectionInventory/index.tsx b/src/components/NftCollectionInventory/index.tsx index b620fb240..ab1fa3dab 100644 --- a/src/components/NftCollectionInventory/index.tsx +++ b/src/components/NftCollectionInventory/index.tsx @@ -14,7 +14,7 @@ import type { NFTItem } from '../../services/ExplorerService/fetcher' const primaryColor = getPrimaryColor() const NftCollectionInventory: React.FC<{ - list: Array + list: NFTItem[] collection: string isLoading: boolean }> = ({ list, collection, isLoading }) => { diff --git a/src/components/NftHolderList/index.tsx b/src/components/NftHolderList/index.tsx index 3521f151c..a177f1c48 100644 --- a/src/components/NftHolderList/index.tsx +++ b/src/components/NftHolderList/index.tsx @@ -8,7 +8,7 @@ import styles from './styles.module.scss' export const primaryColor = getPrimaryColor() const NftHolderList: React.FC<{ - list: Array<{ addr: string; quantity: number }> + list: { addr: string; quantity: number }[] isLoading: boolean }> = ({ list, isLoading }) => { const { t } = useTranslation() diff --git a/src/components/Script/index.tsx b/src/components/Script/index.tsx index 89ccd07ec..195ddb210 100644 --- a/src/components/Script/index.tsx +++ b/src/components/Script/index.tsx @@ -4,6 +4,7 @@ import { ScriptItemPanel, ScriptPanel } from './styled' import HashTag from '../HashTag' import { getContractHashTag } from '../../utils/util' import { HelpTip } from '../HelpTip' +import { Script } from '../../models/Script' const ScriptItem = ({ title, tooltip, children }: { title: string; tooltip?: string; children?: ReactNode }) => ( @@ -16,7 +17,7 @@ const ScriptItem = ({ title, tooltip, children }: { title: string; tooltip?: str ) -const Script = ({ script }: { script: State.Script }) => { +const ScriptComp = ({ script }: { script: Script }) => { const contractHashTag = getContractHashTag(script) const { t } = useTranslation() @@ -38,4 +39,4 @@ const Script = ({ script }: { script: State.Script }) => { ) } -export default Script +export default ScriptComp diff --git a/src/components/Table/styled.tsx b/src/components/Table/styled.tsx index adf87a0dc..f3fbf1438 100644 --- a/src/components/Table/styled.tsx +++ b/src/components/Table/styled.tsx @@ -96,7 +96,7 @@ export const TableMinerContentPanel = styled.div` margin: auto 0; .tableMinerContent { - color: ${(props: { theme: State.Theme }) => props.theme.primary}; + color: ${props => props.theme.primary}; text-decoration: none; } diff --git a/src/components/Transaction/Cellbase/index.tsx b/src/components/Transaction/Cellbase/index.tsx index 7919694d5..0c7e4f193 100644 --- a/src/components/Transaction/Cellbase/index.tsx +++ b/src/components/Transaction/Cellbase/index.tsx @@ -7,8 +7,9 @@ import TransactionCellArrow from '../TransactionCellArrow' import { localeNumberString } from '../../../utils/number' import HelpIcon from '../../../assets/qa_help.png' import styles from './index.module.scss' +import { Cell } from '../../../models/Cell' -const Cellbase = ({ cell, cellType, isDetail }: { cell: State.Cell; cellType: CellType; isDetail?: boolean }) => { +const Cellbase = ({ cell, cellType, isDetail }: { cell: Cell; cellType: CellType; isDetail?: boolean }) => { if (!cell.targetBlockNumber || cell.targetBlockNumber <= 0) { return ( diff --git a/src/components/Transaction/TransactionCellArrow/index.tsx b/src/components/Transaction/TransactionCellArrow/index.tsx index 4b5ebf3e4..47fd63e41 100644 --- a/src/components/Transaction/TransactionCellArrow/index.tsx +++ b/src/components/Transaction/TransactionCellArrow/index.tsx @@ -8,8 +8,9 @@ import LiveCellIcon from '../../../assets/live_cell.png' import LiveCellBlueIcon from '../../../assets/live_cell_blue.png' import { isMainnet } from '../../../utils/chain' import { RightArrowImage, LeftArrowImage } from './styled' +import { Cell } from '../../../models/Cell' -const CellInputIcon = ({ cell }: { cell: State.Cell }) => +const CellInputIcon = ({ cell }: { cell: Cell }) => cell.generatedTxHash ? ( ) : null -const CellOutputIcon = ({ cell }: { cell: State.Cell }) => { +const CellOutputIcon = ({ cell }: { cell: Cell }) => { const { t } = useTranslation() if (cell.status === 'dead') { @@ -37,5 +38,5 @@ const CellOutputIcon = ({ cell }: { cell: State.Cell }) => { ) } -export default ({ cell, cellType }: { cell: State.Cell; cellType: CellType }) => +export default ({ cell, cellType }: { cell: Cell; cellType: CellType }) => cellType === CellType.Input ? : diff --git a/src/components/TransactionItem/TransactionIncome/styled.tsx b/src/components/TransactionItem/TransactionIncome/styled.tsx index c65803210..8df7a6ecd 100644 --- a/src/components/TransactionItem/TransactionIncome/styled.tsx +++ b/src/components/TransactionItem/TransactionIncome/styled.tsx @@ -11,7 +11,7 @@ export const TransactionIncomePanel = styled.div` } ` -export const TransactionCapacityValuePanel = styled.div` +export const TransactionCapacityValuePanel = styled.div<{ increased: boolean }>` height: 36px; border-radius: 18px; background-color: white; @@ -19,7 +19,7 @@ export const TransactionCapacityValuePanel = styled.div` display: flex; align-items: center; justify-content: flex-end; - color: ${(props: { increased: boolean; theme: State.Theme }) => (props.increased ? props.theme.primary : '#FF6347')}; + color: ${props => (props.increased ? props.theme.primary : '#FF6347')}; font-size: 16px; img { diff --git a/src/components/TransactionItem/TransactionItemCell/index.tsx b/src/components/TransactionItem/TransactionItemCell/index.tsx index 0e1a11cf1..15eac0623 100644 --- a/src/components/TransactionItem/TransactionItemCell/index.tsx +++ b/src/components/TransactionItem/TransactionItemCell/index.tsx @@ -29,6 +29,7 @@ import { ReactComponent as BitAccountIcon } from '../../../assets/bit_account.sv import { useBoolean, useIsMobile } from '../../../utils/hook' import CopyTooltipText from '../../Text/CopyTooltipText' import EllipsisMiddle from '../../EllipsisMiddle' +import { Cell, Cell$UDT, UDTInfo } from '../../../models/Cell' const AddressTextWithAlias: FC<{ address: string @@ -65,7 +66,7 @@ const AddressTextWithAlias: FC<{ ) } -const useUdtAmount = (udt: State.UDTInfo) => { +const useUdtAmount = (udt: UDTInfo) => { const { t } = useTranslation() return udt.published ? `${parseUDTAmount(udt.amount, udt.decimal)} ${udt.uan || udt.symbol}` @@ -87,7 +88,7 @@ const WithdrawPopoverItem = ({ ) -const WithdrawPopoverInfo = ({ cell }: { cell: State.Cell }) => { +const WithdrawPopoverInfo = ({ cell }: { cell: Cell }) => { const isMobile = useIsMobile() const { t } = useTranslation() const currentLanguage = useCurrentLanguage() @@ -166,7 +167,7 @@ const WithdrawPopoverInfo = ({ cell }: { cell: State.Cell }) => { ) } -const TransactionCellNervosDao = ({ cell, cellType }: { cell: State.Cell; cellType: CellType }) => { +const TransactionCellNervosDao = ({ cell, cellType }: { cell: Cell; cellType: CellType }) => { const isMobile = useIsMobile() const { t } = useTranslation() return ( @@ -192,7 +193,7 @@ const TransactionCellNervosDao = ({ cell, cellType }: { cell: State.Cell; cellTy ) } -const TransactionCellUDT = ({ cell }: { cell: State.Cell$UDT }) => { +const TransactionCellUDT = ({ cell }: { cell: Cell$UDT }) => { const isMobile = useIsMobile() const { extraInfo } = cell @@ -213,7 +214,7 @@ const TransactionCellUDT = ({ cell }: { cell: State.Cell$UDT }) => { ) } -const TransactionCellCapacity = ({ cell, cellType }: { cell: State.Cell; cellType: CellType }) => { +const TransactionCellCapacity = ({ cell, cellType }: { cell: Cell; cellType: CellType }) => { if (isDaoCell(cell.cellType)) { return } @@ -229,7 +230,7 @@ const TransactionCellCapacity = ({ cell, cellType }: { cell: State.Cell; cellTyp ) } -const TransactionCell = ({ cell, address, cellType }: { cell: State.Cell; address?: string; cellType: CellType }) => { +const TransactionCell = ({ cell, address, cellType }: { cell: Cell; address?: string; cellType: CellType }) => { const isMobile = useIsMobile() const { t } = useTranslation() if (cell.fromCellbase) { diff --git a/src/components/TransactionItem/TransactionItemCell/styled.tsx b/src/components/TransactionItem/TransactionItemCell/styled.tsx index a78197d4d..bb27a9ee5 100644 --- a/src/components/TransactionItem/TransactionItemCell/styled.tsx +++ b/src/components/TransactionItem/TransactionItemCell/styled.tsx @@ -1,11 +1,11 @@ import styled from 'styled-components' -export const TransactionCellPanel = styled.div` +export const TransactionCellPanel = styled.div<{ highLight?: boolean }>` display: flex; align-items: center; justify-content: space-between; margin-top: 16px; - background: ${({ highLight = false }: { highLight?: boolean }) => (highLight ? '' : '#f5f5f5')}; + background: ${({ highLight }) => (highLight ? '' : '#f5f5f5')}; @media (min-width: 750px) { height: 20px; @@ -18,8 +18,7 @@ export const TransactionCellPanel = styled.div` } .transactionCellAddress { - color: ${({ highLight = false, theme }: { highLight?: boolean; theme: State.Theme }) => - highLight ? `${theme.primary}` : '#000000'}; + color: ${({ highLight = false, theme }) => (highLight ? `${theme.primary}` : '#000000')}; font-weight: 500; min-width: 0; width: 100%; @@ -33,11 +32,11 @@ export const TransactionCellPanel = styled.div` } a { - color: ${({ theme }: { theme: State.Theme }) => `${theme.primary}`}; + color: ${({ theme }) => `${theme.primary}`}; } a:hover { - color: ${({ theme }: { theme: State.Theme }) => `${theme.primary}`}; + color: ${({ theme }) => `${theme.primary}`}; } } ` @@ -194,10 +193,10 @@ export const WithdrawItemPanel = styled.div` } a { - color: ${({ theme }: { theme: State.Theme }) => theme.primary}; + color: ${({ theme }) => theme.primary}; } a:hover { - color: ${({ theme }: { theme: State.Theme }) => `${theme.primary}`}; + color: ${({ theme }) => `${theme.primary}`}; } ` diff --git a/src/components/TransactionItem/TransactionItemCellList/index.tsx b/src/components/TransactionItem/TransactionItemCellList/index.tsx index 5d2ed3d8f..db4878f17 100644 --- a/src/components/TransactionItem/TransactionItemCellList/index.tsx +++ b/src/components/TransactionItem/TransactionItemCellList/index.tsx @@ -2,6 +2,8 @@ import { ReactNode } from 'react' import { Link } from 'react-router-dom' import { useTranslation } from 'react-i18next' import TransactionCellListPanel from './styled' +import { Cell } from '../../../models/Cell' +import { Transaction } from '../../../models/Transaction' const MAX_CELL_SHOW_SIZE = 10 @@ -10,9 +12,9 @@ export default ({ transaction, render, }: { - cells: State.Cell[] - transaction: State.Transaction - render: (cell: State.Cell) => ReactNode + cells: Cell[] + transaction: Transaction + render: (cell: Cell) => ReactNode }) => { const { t } = useTranslation() return ( diff --git a/src/components/TransactionItem/TransactionLiteItem/index.tsx b/src/components/TransactionItem/TransactionLiteItem/index.tsx index a0a8439f8..9227ead3f 100644 --- a/src/components/TransactionItem/TransactionLiteItem/index.tsx +++ b/src/components/TransactionItem/TransactionLiteItem/index.tsx @@ -5,8 +5,9 @@ import AddressText from '../../AddressText' import styles from './index.module.scss' import TransactionLiteIncome from '../TransactionLiteIncome' import { useIsMobile, useParsedDate } from '../../../utils/hook' +import { Transaction } from '../../../models/Transaction' -const TransactionLiteItem = ({ transaction, address }: { transaction: State.Transaction; address?: string }) => { +const TransactionLiteItem = ({ transaction, address }: { transaction: Transaction; address?: string }) => { const isMobile = useIsMobile() const { t } = useTranslation() const parsedBlockCreateAt = useParsedDate(transaction.blockTimestamp) diff --git a/src/components/TransactionItem/index.tsx b/src/components/TransactionItem/index.tsx index 1ff620794..aedcc2514 100644 --- a/src/components/TransactionItem/index.tsx +++ b/src/components/TransactionItem/index.tsx @@ -10,6 +10,7 @@ import { FullPanel, TransactionHashBlockPanel, TransactionCellPanel, Transaction import { CellType } from '../../constants/common' import AddressText from '../AddressText' import { useIsLGScreen, useParsedDate } from '../../utils/hook' +import { Transaction } from '../../models/Transaction' export interface CircleCorner { top?: boolean @@ -27,7 +28,7 @@ const TransactionItem = ({ }, scrollIntoViewOnMount, }: { - transaction: State.Transaction + transaction: Transaction address?: string isBlock?: boolean titleCard?: ReactNode | null diff --git a/src/constants/common.ts b/src/constants/common.ts index 97d14b299..ba4292cbe 100644 --- a/src/constants/common.ts +++ b/src/constants/common.ts @@ -96,6 +96,7 @@ export const ChartColor = { secondaryIssuanceColors: ['#484E4E', '#5824FB', '#31EEB3'], liquidityColors: ['#5824FB', '#484E4E', '#31EEB3'], } +export type ChartColorConfig = typeof ChartColor export enum ChainName { Mainnet = 'mirana', diff --git a/src/constants/scripts.ts b/src/constants/scripts.ts index db6312bc2..e2c3dede3 100644 --- a/src/constants/scripts.ts +++ b/src/constants/scripts.ts @@ -1,3 +1,5 @@ +import { Script } from '../models/Script' + export interface ContractHashTag { codeHashes: string[] // The code hashes whose hash type are type in mainnet and testnet are different txHashes: string[] // mainnet and testnet contract tx hashes @@ -7,7 +9,7 @@ export interface ContractHashTag { hashType: string } -export const ScriptTagExtraRules = new Map string>([ +export const ScriptTagExtraRules = new Map string>([ [ 'secp256k1 / multisig', script => (script.args.length === 28 * 2 + 2 ? 'secp256k1 / multisig / locktime' : 'secp256k1 / multisig'), diff --git a/src/models/Address/UDTAccount.ts b/src/models/Address/UDTAccount.ts new file mode 100644 index 000000000..c0b3c7de3 --- /dev/null +++ b/src/models/Address/UDTAccount.ts @@ -0,0 +1,67 @@ +export interface SUDT { + symbol: string + decimal: string + amount: string + typeHash: string + udtIconFile: string + uan?: string + udtType: 'sudt' + collection: undefined + cota: undefined +} + +export interface MNFT { + symbol: string + decimal: string + amount: string + typeHash: string + udtIconFile: string + udtType: 'm_nft_token' + uan: undefined + collection: { + typeHash: string + } + cota: undefined +} + +export interface NRC721 { + symbol: string + amount: string // token id in fact + typeHash: string + udtIconFile: string // base uri with token id in fact + udtType: 'nrc_721_token' + uan: undefined + collection: { + typeHash: string + } + cota: undefined +} + +export interface CoTA { + symbol: string + amount: string + typeHash: string + udtIconFile: string // base uri with token id in fact + udtType: 'cota' + uan: undefined + collection: undefined + cota: { + cotaId: number + tokenId: number + } +} + +export interface Spore { + symbol?: string + amount: string + typeHash: string + udtIconFile: string + udtType: 'spore_cell' + collection: { + typeHash: string | null + } + uan: undefined + cota: undefined +} + +export type UDTAccount = SUDT | MNFT | NRC721 | CoTA | Spore diff --git a/src/models/Address/index.ts b/src/models/Address/index.ts new file mode 100644 index 000000000..4551798bb --- /dev/null +++ b/src/models/Address/index.ts @@ -0,0 +1,31 @@ +import { Script } from '../Script' +import { UDTAccount } from './UDTAccount' + +export interface LockInfo { + status: 'locked' | 'unlocked' + epochNumber: string + epochIndex: string + estimatedUnlockTime: string +} + +export interface Address { + addressHash: string + lockHash: string + balance: string + balanceOccupied: string + transactionsCount: number + lockScript: Script + pendingRewardBlocksCount: number + type: 'Address' | 'LockHash' | '' + daoDeposit: number + interest: number + daoCompensation: number + lockInfo: LockInfo + liveCellsCount: string + minedBlocksCount: string + isSpecial: boolean + specialAddress: string + udtAccounts?: UDTAccount[] +} + +export * from './UDTAccount' diff --git a/src/models/Block/index.ts b/src/models/Block/index.ts new file mode 100644 index 000000000..2c21aeb33 --- /dev/null +++ b/src/models/Block/index.ts @@ -0,0 +1,33 @@ +export interface Block { + blockHash: string + number: number + transactionsCount: number + proposalsCount: number + unclesCount: number + uncleBlockHashes: string[] + reward: string + rewardStatus: 'pending' | 'issued' + totalTransactionFee: string + receivedTxFee: string + receivedTxFeeStatus: 'pending' | 'calculated' + totalCellCapacity: string + minerHash: string + minerMessage: string + timestamp: number + difficulty: string + epoch: number + length: string + startNumber: number + version: number + nonce: string + transactionsRoot: string + blockIndexInEpoch: string + minerReward: string + liveCellChanges: string + size: number + largestBlockInEpoch: number + largestBlock: number + cycles: number | null + maxCyclesInEpoch: number | null + maxCycles: number | null +} diff --git a/src/models/Cell/index.ts b/src/models/Cell/index.ts new file mode 100644 index 000000000..0f2961d1a --- /dev/null +++ b/src/models/Cell/index.ts @@ -0,0 +1,112 @@ +import { Script } from '../Script' + +export interface UDTInfo { + symbol: string + amount: string + decimal: string + typeHash: string + published: boolean + uan?: string +} + +export interface Nrc721TokenInfo { + amount: string + symbol: string +} + +export interface NftIssuer { + issuerName: string +} + +export interface NftClass { + className: string + total: string +} + +export interface NftToken { + className: string + tokenId: string + total: string +} + +export interface CellInfo { + lock: Script + type: Script + data: string +} + +export interface Cell$Base { + id: number + addressHash: string + capacity: string + occupiedCapacity: string + fromCellbase: boolean + generatedTxHash: string + targetBlockNumber: number + baseReward: string + secondaryReward: string + commitReward: string + proposalReward: string + consumedTxHash: string + status: 'live' | 'dead' + isGenesisOutput: boolean + cellIndex: string + compensationStartedBlockNumber: number + compensationEndedBlockNumber: number + compensationStartedTimestamp: number + compensationEndedTimestamp: number + lockedUntilBlockNumber: number + lockedUntilBlockTimestamp: number + interest: string + daoTypeHash: string + cellInfo: CellInfo + since?: { + raw: string + median_timestamp?: string + } +} + +export interface Cell$NoExtra extends Cell$Base { + cellType: + | 'normal' + | 'nervos_dao_deposit' + | 'nervos_dao_withdrawing' + | 'cota_registry' + | 'cota_regular' + // TODO: Perhaps nft_transfer, simple_transfer, and nft_mint should be removed, as they cannot be found + // at https://github.com/nervosnetwork/ckb-explorer/blob/develop/app/utils/ckb_utils.rb#L380. + | 'nft_transfer' + | 'simple_transfer' + | 'nft_mint' + | 'spore_cluster' + | 'spore_cell' + | 'nrc_721_factory' + extraInfo?: never +} + +export interface Cell$UDT extends Cell$Base { + cellType: 'udt' + extraInfo: UDTInfo +} + +export interface Cell$NftIssuer extends Cell$Base { + cellType: 'm_nft_issuer' + extraInfo: NftIssuer +} + +export interface Cell$NftClass extends Cell$Base { + cellType: 'm_nft_class' + extraInfo: NftClass +} + +export interface Cell$NftToken extends Cell$Base { + cellType: 'm_nft_token' + extraInfo: NftToken +} + +export interface Cell$Nrc721Token extends Cell$Base { + cellType: 'nrc_721_token' + extraInfo: Nrc721TokenInfo +} + +export type Cell = Cell$NoExtra | Cell$UDT | Cell$NftIssuer | Cell$NftClass | Cell$NftToken | Cell$Nrc721Token diff --git a/src/models/Script/index.ts b/src/models/Script/index.ts new file mode 100644 index 000000000..01698a5c8 --- /dev/null +++ b/src/models/Script/index.ts @@ -0,0 +1,5 @@ +export interface Script { + codeHash: string + args: string + hashType: string +} diff --git a/src/models/Transaction/index.ts b/src/models/Transaction/index.ts new file mode 100644 index 000000000..33cb47d57 --- /dev/null +++ b/src/models/Transaction/index.ts @@ -0,0 +1,36 @@ +import { Cell } from '../Cell' + +export interface CellDep { + depType: string + outPoint: { + index: string + txHash: string + } +} + +export interface Transaction { + transactionHash: string + blockNumber: number + blockTimestamp: number + transactionFee: string + income: string + isCellbase: boolean + targetBlockNumber: number + version: number + displayInputs: Cell[] + displayOutputs: Cell[] + cellDeps: CellDep[] + headerDeps: string[] + witnesses: string[] + liveCellChanges: string + capacityInvolved: string + txStatus: string + detailedMessage: string + bytes: number + largestTxInEpoch: number + largestTx: number + cycles: number | null + maxCyclesInEpoch: number | null + maxCycles: number | null + createTimestamp?: number +} diff --git a/src/models/UDT/index.ts b/src/models/UDT/index.ts new file mode 100644 index 000000000..40e5a49ff --- /dev/null +++ b/src/models/UDT/index.ts @@ -0,0 +1,19 @@ +import { Script } from '../Script' + +export interface UDT { + symbol: string + fullName: string + iconFile: string + published: boolean + description: string + totalAmount: string + addressesCount: string + decimal: string + h24CkbTransactionsCount: string + createdAt: string + typeHash: string + issuerAddress: string + typeScript: Script + displayName?: string + uan?: string +} diff --git a/src/pages/Address/AddressComp.tsx b/src/pages/Address/AddressComp.tsx index 747238fb8..ff6b7ed8a 100644 --- a/src/pages/Address/AddressComp.tsx +++ b/src/pages/Address/AddressComp.tsx @@ -48,8 +48,10 @@ import { LayoutLiteProfessional } from '../../constants/common' import { omit } from '../../utils/object' import { CsvExport } from '../../components/CsvExport' import PaginationWithRear from '../../components/PaginationWithRear' +import { Transaction } from '../../models/Transaction' +import { Address, SUDT, UDTAccount } from '../../models/Address' -const addressAssetInfo = (address: State.Address, useMiniStyle: boolean, t: TFunction) => { +const addressAssetInfo = (address: Address, useMiniStyle: boolean, t: TFunction) => { const items = [ { title: '', @@ -92,7 +94,7 @@ const addressAssetInfo = (address: State.Address, useMiniStyle: boolean, t: TFun return items } -const UDT_LABEL: Record = { +const UDT_LABEL: Record = { sudt: 'sudt', m_nft_token: 'm nft', nrc_721_token: 'nrc 721', @@ -100,7 +102,7 @@ const UDT_LABEL: Record = { spore_cell: 'Spore', } -const AddressUDTItem = ({ udtAccount }: { udtAccount: State.UDTAccount }) => { +const AddressUDTItem = ({ udtAccount }: { udtAccount: UDTAccount }) => { const { t } = useTranslation() const { symbol, uan, amount, udtIconFile, typeHash, udtType, collection, cota } = udtAccount const isSudt = udtType === 'sudt' @@ -145,7 +147,7 @@ const AddressUDTItem = ({ udtAccount }: { udtAccount: State.UDTAccount }) => { switch (true) { case isSudt: { - property = parseUDTAmount(amount, (udtAccount as State.SUDT).decimal) + property = parseUDTAmount(amount, (udtAccount as SUDT).decimal) href = `/sudt/${typeHash}` break } @@ -196,7 +198,7 @@ const lockScriptIcon = (show: boolean) => { return isMainnet() ? ArrowDownIcon : ArrowDownBlueIcon } -const useAddressInfo = ({ liveCellsCount, minedBlocksCount, type, addressHash, lockInfo }: State.Address) => { +const useAddressInfo = ({ liveCellsCount, minedBlocksCount, type, addressHash, lockInfo }: Address) => { const { t } = useTranslation() const items: OverviewItemData[] = [ { @@ -237,7 +239,7 @@ const useAddressInfo = ({ liveCellsCount, minedBlocksCount, type, addressHash, l return items } -const AddressLockScript: FC<{ address: State.Address }> = ({ address }) => { +const AddressLockScript: FC<{ address: Address }> = ({ address }) => { const [showLock, setShowLock] = useState(false) const { t } = useTranslation() @@ -254,7 +256,7 @@ const AddressLockScript: FC<{ address: State.Address }> = ({ address }) => { ) } -export const AddressOverview: FC<{ address: State.Address }> = ({ address }) => { +export const AddressOverview: FC<{ address: Address }> = ({ address }) => { const isLG = useIsLGScreen() const { t } = useTranslation() const { udtAccounts = [] } = address @@ -317,7 +319,7 @@ export const AddressTransactions = ({ timeOrderBy, }: { address: string - transactions: State.Transaction[] + transactions: Transaction[] transactionsTotal: number timeOrderBy: OrderByType }) => { @@ -403,12 +405,12 @@ export const AddressTransactions = ({
{t('transaction.capacity_change')}
)} - {txList.map((transaction: State.Transaction) => ( + {txList.map((transaction: Transaction) => ( ))} ) : ( - txList.map((transaction: State.Transaction, index: number) => ( + txList.map((transaction: Transaction, index: number) => ( = ({ block }) => { +export const BlockOverview: FC<{ block: Block }> = ({ block }) => { const isMobile = useIsMobile() const { t } = useTranslation() const tipBlockNumber = useLatestBlockNumber() @@ -321,7 +323,7 @@ export const BlockComp = ({ onPageChange: (page: number) => void currentPage: number pageSize: number - transactions: State.Transaction[] + transactions: Transaction[] total: number }) => { const { t } = useTranslation() diff --git a/src/pages/BlockDetail/state.ts b/src/pages/BlockDetail/state.ts index 6abedbafc..5c1354e98 100644 --- a/src/pages/BlockDetail/state.ts +++ b/src/pages/BlockDetail/state.ts @@ -1,4 +1,6 @@ -export const defaultBlockInfo: State.Block = { +import { Block } from '../../models/Block' + +export const defaultBlockInfo: Block = { blockHash: '', number: 0, transactionsCount: 0, diff --git a/src/pages/BlockList/index.tsx b/src/pages/BlockList/index.tsx index 81609378a..b99d0f578 100644 --- a/src/pages/BlockList/index.tsx +++ b/src/pages/BlockList/index.tsx @@ -20,6 +20,7 @@ import { ReactComponent as SortIcon } from '../../assets/sort_icon.svg' import { CsvExport } from '../../components/CsvExport' import PaginationWithRear from '../../components/PaginationWithRear' import styles from './styles.module.scss' +import { Block } from '../../models/Block' const BlockValueItem = ({ value, to }: { value: string; to: string }) => ( @@ -45,7 +46,7 @@ interface TableContentData { const LoadingSection = () =>
Loading...
-const getTableContentDataList = (block: State.Block, index: number, page: number, isMaxW: boolean) => { +const getTableContentDataList = (block: Block, index: number, page: number, isMaxW: boolean) => { const blockReward = index < DELAY_BLOCK_NUMBER && page === 1 ? ( @@ -82,9 +83,9 @@ const getTableContentDataList = (block: State.Block, index: number, page: number ] as TableContentData[] } -const BlockCardGroup: FC<{ blocks: State.Block[]; isFirstPage: boolean }> = ({ blocks, isFirstPage }) => { +const BlockCardGroup: FC<{ blocks: Block[]; isFirstPage: boolean }> = ({ blocks, isFirstPage }) => { const { t } = useTranslation() - const items: ItemCardData[] = [ + const items: ItemCardData[] = [ { title: t('home.height'), render: block => , @@ -245,7 +246,7 @@ export default () => { ) : ( blockList.map( - (block: State.Block, blockIndex: number) => + (block: Block, blockIndex: number) => block && ( {getTableContentDataList(block, blockIndex, currentPage, isMaxW).map( diff --git a/src/pages/ExportTransactions/index.tsx b/src/pages/ExportTransactions/index.tsx index ea6db962a..ad4986544 100644 --- a/src/pages/ExportTransactions/index.tsx +++ b/src/pages/ExportTransactions/index.tsx @@ -15,7 +15,7 @@ import { ReactComponent as BlockIcon } from '../../assets/block_icon.svg' import { ReactComponent as ErrorIcon } from '../../assets/error_icon.svg' import { ReactComponent as SuccessIcon } from '../../assets/success_icon.svg' import { omit } from '../../utils/object' -import { explorerService } from '../../services/ExplorerService' +import { SupportedExportTransactionType, explorerService } from '../../services/ExplorerService' const ExportTransactions = () => { const [t, { language }] = useTranslation() @@ -31,7 +31,7 @@ const ExportTransactions = () => { 'from-height': fromHeightStr, 'to-height': toHeightStr, } = useSearchParams('type', 'id', 'tab', 'start-date', 'end-date', 'from-height', 'to-height') - function isTransactionCsvExportType(s?: string): s is State.TransactionCsvExportType { + function isTransactionCsvExportType(s?: string): s is SupportedExportTransactionType { return !!s && ['address_transactions', 'blocks', 'udts', 'nft'].includes(s) } diff --git a/src/pages/FeeRateTracker/FeeRateTrackerComp.tsx b/src/pages/FeeRateTracker/FeeRateTrackerComp.tsx index df348d11b..ffb8f0f1e 100644 --- a/src/pages/FeeRateTracker/FeeRateTrackerComp.tsx +++ b/src/pages/FeeRateTracker/FeeRateTrackerComp.tsx @@ -103,7 +103,7 @@ export const ConfirmationTimeFeeRateChart = ({ transactionFeeRates: FeeRateTracker.TransactionFeeRate[] }) => { const { t } = useTranslation() - const data = transactionFeeRates.reduce>>((acc, cur) => { + const data = transactionFeeRates.reduce((acc, cur) => { if (!cur.confirmationTime) { return acc } diff --git a/src/pages/Home/AverageBlockTimeChart/index.tsx b/src/pages/Home/AverageBlockTimeChart/index.tsx index 993ff8442..10017f851 100644 --- a/src/pages/Home/AverageBlockTimeChart/index.tsx +++ b/src/pages/Home/AverageBlockTimeChart/index.tsx @@ -9,16 +9,13 @@ import SmallLoading from '../../../components/Loading/SmallLoading' import { HomeChartLink, ChartLoadingPanel } from './styled' import ChartNoDataImage from '../../../assets/chart_no_data_white.png' import { useChartQueryWithCache, useIsLGScreen } from '../../../utils/hook' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' import { ReactChartCore } from '../../StatisticsChart/common' const useOption = () => { const { t } = useTranslation() - return ( - statisticAverageBlockTimes: State.StatisticAverageBlockTime[], - useMiniStyle: boolean, - ): echarts.EChartOption => { + return (statisticAverageBlockTimes: ChartItem.AverageBlockTime[], useMiniStyle: boolean): echarts.EChartOption => { return { color: ['#ffffff'], title: { diff --git a/src/pages/Home/HashRateChart/index.tsx b/src/pages/Home/HashRateChart/index.tsx index 1158cf08c..5eafe6f2b 100644 --- a/src/pages/Home/HashRateChart/index.tsx +++ b/src/pages/Home/HashRateChart/index.tsx @@ -10,13 +10,13 @@ import SmallLoading from '../../../components/Loading/SmallLoading' import { HomeChartLink, ChartLoadingPanel } from './styled' import ChartNoDataImage from '../../../assets/chart_no_data_white.png' import { useChartQueryWithCache, useIsLGScreen } from '../../../utils/hook' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' import { ReactChartCore } from '../../StatisticsChart/common' const useOption = () => { const { t } = useTranslation() - return (statisticHashRates: State.StatisticHashRate[], useMiniStyle: boolean): echarts.EChartOption => { + return (statisticHashRates: ChartItem.HashRate[], useMiniStyle: boolean): echarts.EChartOption => { return { color: ['#ffffff'], title: { diff --git a/src/pages/Home/TableCard/index.tsx b/src/pages/Home/TableCard/index.tsx index 777f9fee3..c734407a8 100644 --- a/src/pages/Home/TableCard/index.tsx +++ b/src/pages/Home/TableCard/index.tsx @@ -9,9 +9,11 @@ import { BlockRewardPlusPanel, BlockRewardPanel, BlockCardPanel, TransactionCard import AddressText from '../../../components/AddressText' import styles from './index.module.scss' import { useParsedDate } from '../../../utils/hook' +import { Block } from '../../../models/Block' +import { Transaction } from '../../../models/Transaction' // eslint-disable-next-line no-underscore-dangle -const _BlockCardItem: FC<{ block: State.Block; isDelayBlock?: boolean }> = ({ block, isDelayBlock }) => { +const _BlockCardItem: FC<{ block: Block; isDelayBlock?: boolean }> = ({ block, isDelayBlock }) => { const { t } = useTranslation() const liveCellChanges = Number(block.liveCellChanges) const blockReward = isDelayBlock ? ( @@ -74,7 +76,7 @@ export const BlockCardItem = memo( // eslint-disable-next-line no-underscore-dangle const _TransactionCardItem: FC<{ - transaction: State.Transaction + transaction: Transaction tipBlockNumber: number }> = ({ transaction, tipBlockNumber }) => { const { t } = useTranslation() diff --git a/src/pages/Home/index.tsx b/src/pages/Home/index.tsx index eb773c3cd..7d5e7650e 100644 --- a/src/pages/Home/index.tsx +++ b/src/pages/Home/index.tsx @@ -38,6 +38,8 @@ import styles from './index.module.scss' import { RouteState } from '../../routes/state' import { explorerService, useLatestBlockNumber, useStatistics } from '../../services/ExplorerService' import { useShowSearchBarInHeader } from '../../components/Header' +import { Block } from '../../models/Block' +import { Transaction } from '../../models/Transaction' interface BlockchainData { name: string @@ -70,8 +72,10 @@ const parseHashRate = (hashRate: string | undefined) => (hashRate ? handleHashRa const parseBlockTime = (blockTime: string | undefined) => (blockTime ? parseTime(Number(blockTime)) : '- -') -const useBlockchainDataList = (statistics: State.Statistics, isMobile: boolean, isLG: boolean): BlockchainData[] => { +const useBlockchainDataList = (isMobile: boolean, isLG: boolean): BlockchainData[] => { const { t } = useTranslation() + const statistics = useStatistics() + return [ { name: t('blockchain.latest_block'), @@ -153,7 +157,7 @@ const HomeHeaderTopPanel: FC = memo(() => { ) }) -const BlockList: FC<{ blocks: State.Block[] }> = memo(({ blocks }) => { +const BlockList: FC<{ blocks: Block[] }> = memo(({ blocks }) => { return blocks.length > 0 ? ( <> {blocks.map((block, index) => ( @@ -168,7 +172,7 @@ const BlockList: FC<{ blocks: State.Block[] }> = memo(({ blocks }) => { ) }) -const TransactionList: FC<{ transactions: State.Transaction[]; tipBlockNumber: number }> = memo( +const TransactionList: FC<{ transactions: Transaction[]; tipBlockNumber: number }> = memo( ({ transactions, tipBlockNumber }) => { return transactions.length > 0 ? ( <> @@ -190,7 +194,6 @@ export default () => { const { t } = useTranslation() const isLG = useIsLGScreen() const history = useHistory() - const statistics = useStatistics() const tipBlockNumber = useLatestBlockNumber() const blocksQuery = useQuery( @@ -234,7 +237,7 @@ export default () => { handleBlockchainAlert() }, BLOCKCHAIN_ALERT_POLLING_TIME) - const blockchainDataList = useBlockchainDataList(statistics, isMobile, isLG) + const blockchainDataList = useBlockchainDataList(isMobile, isLG) return ( diff --git a/src/pages/NervosDao/DaoOverview/index.tsx b/src/pages/NervosDao/DaoOverview/index.tsx index d27550d82..546c161cf 100644 --- a/src/pages/NervosDao/DaoOverview/index.tsx +++ b/src/pages/NervosDao/DaoOverview/index.tsx @@ -29,6 +29,9 @@ import { ReactChartCore } from '../../StatisticsChart/common' import { HelpTip } from '../../../components/HelpTip' import { ChartColor } from '../../../constants/common' import { assertNotArray } from '../../../utils/chart' +import { APIReturn } from '../../../services/ExplorerService' + +type NervosDaoInfo = APIReturn<'fetchNervosDao'> interface NervosDaoItemContent { title: string @@ -67,7 +70,7 @@ const daoIcon = (symbol: 'positive' | 'negative' | 'zero' | undefined) => { } } -const useNervosDaoItemContents = (nervosDao: State.NervosDao): NervosDaoItemContent[] => { +const useNervosDaoItemContents = (nervosDao: NervosDaoInfo): NervosDaoItemContent[] => { const { t } = useTranslation() return [ { @@ -133,7 +136,7 @@ const NervosDaoLeftItem = ({ item, firstLine }: { item: NervosDaoItemContent; fi ) -const NervosDaoOverviewLeftComp: FC<{ nervosDao: State.NervosDao }> = ({ nervosDao }) => { +const NervosDaoOverviewLeftComp: FC<{ nervosDao: NervosDaoInfo }> = ({ nervosDao }) => { const isMobile = useIsMobile() const leftItems = useNervosDaoItemContents(nervosDao) @@ -183,7 +186,7 @@ const NervosDaoOverviewLeftComp: FC<{ nervosDao: State.NervosDao }> = ({ nervosD ) } -const useOption = (nervosDao: State.NervosDao, colors: string[], isMobile: boolean): echarts.EChartOption => { +const useOption = (nervosDao: NervosDaoInfo, colors: string[], isMobile: boolean): echarts.EChartOption => { const { t } = useTranslation() const { miningReward, depositCompensation, treasuryAmount } = nervosDao const sum = @@ -281,13 +284,13 @@ const NervosDaoPieItem = ({ item }: { item: NervosDaoPieItemContent }) => ( ) -export default ({ nervosDao }: { nervosDao: State.NervosDao }) => { +export default ({ nervosDao }: { nervosDao: NervosDaoInfo }) => { const isMobile = useIsMobile() const { t } = useTranslation() const isExactLG = useIsLGScreen(true) const nervosDaoPieItemContents = useCallback( - (nervosDao: State.NervosDao): NervosDaoPieItemContent[] => [ + (nervosDao: NervosDaoInfo): NervosDaoPieItemContent[] => [ { title: t('nervos_dao.mining_reward'), content: , diff --git a/src/pages/NervosDao/DaoOverview/styled.tsx b/src/pages/NervosDao/DaoOverview/styled.tsx index e6eda928a..78b2f80a1 100644 --- a/src/pages/NervosDao/DaoOverview/styled.tsx +++ b/src/pages/NervosDao/DaoOverview/styled.tsx @@ -147,7 +147,7 @@ export const DaoOverviewPieItemsPanel = styled.div` } ` -export const DaoOverviewLeftItemPanel = styled.div` +export const DaoOverviewLeftItemPanel = styled.div<{ symbol?: string; hasChange?: boolean; hasTooltip?: boolean }>` display: flex; flex-direction: column; flex: 1; @@ -177,8 +177,7 @@ export const DaoOverviewLeftItemPanel = styled.div` white-space: nowrap; @media (max-width: 1440px) { - max-width: ${(props: { symbol?: string; hasChange?: boolean; hasTooltip?: boolean }) => - props.hasChange ? '130px' : '200px'}; + max-width: ${props => (props.hasChange ? '130px' : '200px')}; } @media (max-width: 1200px) { @@ -187,13 +186,13 @@ export const DaoOverviewLeftItemPanel = styled.div` @media (max-width: 750px) { font-size: 12px; - max-width: ${(props: { hasChange?: boolean }) => (props.hasChange ? '90px' : '200px')}; + max-width: ${props => (props.hasChange ? '90px' : '200px')}; } } .daoOverviewItemChangeIcon { - width: ${(props: { symbol?: string }) => (props.symbol === 'zero' ? '10px' : '7px')}; - height: ${(props: { symbol?: string }) => (props.symbol === 'zero' ? '7px' : '10px')}; + width: ${props => (props.symbol === 'zero' ? '10px' : '7px')}; + height: ${props => (props.symbol === 'zero' ? '7px' : '10px')}; margin-left: 5px; margin-right: 3px; } @@ -201,8 +200,7 @@ export const DaoOverviewLeftItemPanel = styled.div` .daoOverviewItemChange { font-size: 12px; font-weight: bold; - color: ${(props: { symbol?: string; theme: State.Theme }) => - props.symbol === 'negative' ? '#FF464F' : props.theme.primary}; + color: ${props => (props.symbol === 'negative' ? '#FF464F' : props.theme.primary)}; cursor: default; @media (max-width: 750px) { diff --git a/src/pages/NervosDao/DaoTransactions/index.tsx b/src/pages/NervosDao/DaoTransactions/index.tsx index a23580018..b615aae07 100644 --- a/src/pages/NervosDao/DaoTransactions/index.tsx +++ b/src/pages/NervosDao/DaoTransactions/index.tsx @@ -4,6 +4,7 @@ import { TransactionsPagination, DAONoResultPanel } from './styled' import Pagination from '../../../components/Pagination' import { PageParams } from '../../../constants/common' import { deprecatedAddrToNewAddr } from '../../../utils/util' +import { Transaction } from '../../../models/Transaction' export default ({ currentPage = 1, @@ -15,7 +16,7 @@ export default ({ }: { currentPage: number pageSize: number - transactions: State.Transaction[] + transactions: Transaction[] total: number onPageChange: (page: number) => void filterNoResult?: boolean @@ -45,7 +46,7 @@ export default ({ return ( <> {txList.map( - (transaction: State.Transaction, index: number) => + (transaction: Transaction, index: number) => transaction && ( { return ( @@ -29,10 +32,10 @@ const AddressTextCol = ({ address }: { address: string }) => { ) } -const DepositorCardGroup: FC<{ depositors: (State.NervosDaoDepositor & { rank: number })[] }> = ({ depositors }) => { +const DepositorCardGroup: FC<{ depositors: RankedDepositor[] }> = ({ depositors }) => { const { t } = useTranslation() - const items: ItemCardData[] = [ + const items: ItemCardData[] = [ { title: t('nervos_dao.dao_title_rank'), render: depositor => depositor.rank, @@ -54,9 +57,9 @@ const DepositorCardGroup: FC<{ depositors: (State.NervosDaoDepositor & { rank: n return idx} /> } -export default ({ depositors, filter }: { depositors: State.NervosDaoDepositor[]; filter?: string }) => { +export default ({ depositors, filter }: { depositors: NervosDaoDepositor[]; filter?: string }) => { const { t } = useTranslation() - const rankedDepositors = depositors.map((depositor, index) => ({ + const rankedDepositors: RankedDepositor[] = depositors.map((depositor, index) => ({ ...depositor, rank: index + 1, })) diff --git a/src/pages/NervosDao/state.ts b/src/pages/NervosDao/state.ts index 92fd9cd1c..1f4aa3d08 100644 --- a/src/pages/NervosDao/state.ts +++ b/src/pages/NervosDao/state.ts @@ -1,4 +1,6 @@ -export const defaultNervosDaoInfo: State.NervosDao = { +import { APIReturn } from '../../services/ExplorerService' + +export const defaultNervosDaoInfo: APIReturn<'fetchNervosDao'> = { totalDeposit: '', depositorsCount: '', depositChanges: '', diff --git a/src/pages/NftCollectionInfo/index.tsx b/src/pages/NftCollectionInfo/index.tsx index 4decc3f1d..9ef1e5a7b 100644 --- a/src/pages/NftCollectionInfo/index.tsx +++ b/src/pages/NftCollectionInfo/index.tsx @@ -18,7 +18,7 @@ import NftHolderList from '../../components/NftHolderList' import Pagination from '../../components/Pagination' const tabs = ['transfers', 'holders', 'inventory'] -function getFilterList(t: TFunction): Array> { +function getFilterList(t: TFunction): Record<'title' | 'value', string>[] { return [ { value: 'mint', diff --git a/src/pages/NftCollections/List.tsx b/src/pages/NftCollections/List.tsx index ad80f999d..1e60c7a85 100644 --- a/src/pages/NftCollections/List.tsx +++ b/src/pages/NftCollections/List.tsx @@ -14,7 +14,7 @@ import type { NFTCollection } from '../../services/ExplorerService/fetcher' type NftSortField = 'transactions' | 'holder' | 'minted' const primaryColor = getPrimaryColor() -function useFilterList(): Array> { +function useFilterList(): Record<'title' | 'value', string>[] { const { t } = useTranslation() return [ { @@ -142,7 +142,7 @@ const TypeInfo: React.FC<{ nft: NFTCollection }> = ({ nft: item }) => { ) } -export const ListOnDesktop: React.FC<{ isLoading: boolean; list: Array }> = ({ list, isLoading }) => { +export const ListOnDesktop: React.FC<{ isLoading: boolean; list: NFTCollection[] }> = ({ list, isLoading }) => { const { t } = useTranslation() return ( @@ -245,7 +245,7 @@ export const ListOnDesktop: React.FC<{ isLoading: boolean; list: Array }> = ({ list, isLoading }) => { +export const ListOnMobile: React.FC<{ isLoading: boolean; list: NFTCollection[] }> = ({ list, isLoading }) => { const { t } = useTranslation() return (
diff --git a/src/pages/ScriptList/index.tsx b/src/pages/ScriptList/index.tsx index d35099c84..1fde16316 100644 --- a/src/pages/ScriptList/index.tsx +++ b/src/pages/ScriptList/index.tsx @@ -188,7 +188,7 @@ export const scripts = new Map([ ], ]) -const keysWithLinkValueInScript: Array = ['rfc', 'code', 'deprecated', 'website'] +const keysWithLinkValueInScript: (keyof ScriptAttributes)[] = ['rfc', 'code', 'deprecated', 'website'] const ScriptList: FC = () => { const { t } = useTranslation() diff --git a/src/pages/SimpleUDT/SimpleUDTComp.tsx b/src/pages/SimpleUDT/SimpleUDTComp.tsx index 1ffb77201..efa5e0402 100644 --- a/src/pages/SimpleUDT/SimpleUDTComp.tsx +++ b/src/pages/SimpleUDT/SimpleUDTComp.tsx @@ -12,6 +12,8 @@ import styles from './styles.module.scss' import AddressText from '../../components/AddressText' import PaginationWithRear from '../../components/PaginationWithRear' import { CsvExport } from '../../components/CsvExport' +import { Transaction } from '../../models/Transaction' +import { UDT } from '../../models/UDT' const useAddressContent = (address: string) => { const { t } = useTranslation() @@ -41,7 +43,7 @@ const useAddressContent = (address: string) => { ) } -const useSimpleUDTInfo = (udt: State.UDT) => { +const useSimpleUDTInfo = (udt: UDT) => { const { t } = useTranslation() const { displayName, uan, fullName, issuerAddress, symbol, addressesCount, decimal, totalAmount } = udt return [ @@ -73,7 +75,7 @@ const useSimpleUDTInfo = (udt: State.UDT) => { ] as OverviewItemData[] } -export const SimpleUDTOverview = ({ children, udt }: { children: ReactNode; udt: State.UDT }) => { +export const SimpleUDTOverview = ({ children, udt }: { children: ReactNode; udt: UDT }) => { return ( {children} @@ -92,7 +94,7 @@ export const SimpleUDTComp = ({ }: { currentPage: number pageSize: number - transactions: State.Transaction[] + transactions: Transaction[] total: number onPageChange: (page: number) => void filterNoResult?: boolean @@ -112,7 +114,7 @@ export const SimpleUDTComp = ({ <> {transactions.map( - (transaction: State.Transaction, index: number) => + (transaction: Transaction, index: number) => transaction && ( { if (show) { @@ -70,7 +71,7 @@ export const SimpleUDT = () => { type, }) - const ensureCellAddrIsNewFormat = (cell: State.Cell) => ({ + const ensureCellAddrIsNewFormat = (cell: Cell) => ({ ...cell, addressHash: deprecatedAddrToNewAddr(cell.addressHash), }) diff --git a/src/pages/SimpleUDT/state.ts b/src/pages/SimpleUDT/state.ts index f0a29fdcd..89e1c867f 100644 --- a/src/pages/SimpleUDT/state.ts +++ b/src/pages/SimpleUDT/state.ts @@ -1,4 +1,6 @@ -export const defaultUDTInfo: State.UDT = { +import { UDT } from '../../models/UDT' + +export const defaultUDTInfo: UDT = { symbol: '', fullName: '', totalAmount: '0', diff --git a/src/pages/StatisticsChart/activities/AddressBalanceRank.tsx b/src/pages/StatisticsChart/activities/AddressBalanceRank.tsx index 1fbef2947..619c4de9f 100644 --- a/src/pages/StatisticsChart/activities/AddressBalanceRank.tsx +++ b/src/pages/StatisticsChart/activities/AddressBalanceRank.tsx @@ -6,11 +6,12 @@ import { DATA_ZOOM_CONFIG, assertIsArray, parseNumericAbbr } from '../../../util import { shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' import { localeNumberString } from '../../../utils/number' import { tooltipColor, tooltipWidth, SmartChartPage, SmartChartPageProps } from '../common' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' import { useAdaptPCEllipsis } from '../../../utils/hook' +import { ChartColorConfig } from '../../../constants/common' -const getAddressWithRanking = (statisticAddressBalanceRanks: State.StatisticAddressBalanceRank[], ranking?: string) => { +const getAddressWithRanking = (statisticAddressBalanceRanks: ChartItem.AddressBalanceRank[], ranking?: string) => { if (!ranking) return '' const addressBalanceRank = statisticAddressBalanceRanks.find(rank => rank.ranking === ranking) return addressBalanceRank ? addressBalanceRank.address : '' @@ -20,8 +21,8 @@ const useOption = () => { const { t } = useTranslation() const currentLanguage = useCurrentLanguage() return ( - statisticAddressBalanceRanks: State.StatisticAddressBalanceRank[], - chartColor: State.ChartColor, + statisticAddressBalanceRanks: ChartItem.AddressBalanceRank[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, getAdaptAddressText: (address: string) => string, @@ -104,7 +105,7 @@ const useOption = () => { } } -const toCSV = (statisticAddressBalanceRanks: State.StatisticAddressBalanceRank[]) => +const toCSV = (statisticAddressBalanceRanks: ChartItem.AddressBalanceRank[]) => statisticAddressBalanceRanks ? statisticAddressBalanceRanks.map(data => [data.ranking, shannonToCkbDecimal(data.balance, 8)]) : [] @@ -113,9 +114,7 @@ export const AddressBalanceRankChart = ({ isThumbnail = false }: { isThumbnail?: const history = useHistory() const [t] = useTranslation() - const [statisticAddressBalanceRanks, setStatisticAddressBalanceRanks] = useState( - [], - ) + const [statisticAddressBalanceRanks, setStatisticAddressBalanceRanks] = useState([]) const handleClick = useCallback( (param: echarts.CallbackDataParams) => { if (param && param.name && statisticAddressBalanceRanks.length > 0) { @@ -130,7 +129,7 @@ export const AddressBalanceRankChart = ({ isThumbnail = false }: { isThumbnail?: const adaptPCEllipsis = useAdaptPCEllipsis(60) const parseOption = useOption() - const getEChartOption: SmartChartPageProps['getEChartOption'] = useCallback( + const getEChartOption: SmartChartPageProps['getEChartOption'] = useCallback( (...args) => parseOption(...args, address => adaptPCEllipsis(address, 6)), [adaptPCEllipsis, parseOption], ) diff --git a/src/pages/StatisticsChart/activities/AddressCount.tsx b/src/pages/StatisticsChart/activities/AddressCount.tsx index b933c96f1..66b0af4f7 100644 --- a/src/pages/StatisticsChart/activities/AddressCount.tsx +++ b/src/pages/StatisticsChart/activities/AddressCount.tsx @@ -3,13 +3,14 @@ import { useTranslation } from 'react-i18next' import { DATA_ZOOM_CONFIG, assertIsArray, handleAxis } from '../../../utils/chart' import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' import { useCurrentLanguage } from '../../../utils/i18n' +import { ChartColorConfig } from '../../../constants/common' const useOption = ( - statisticAddressCounts: State.StatisticAddressCount[], - chartColor: State.ChartColor, + statisticAddressCounts: ChartItem.AddressCount[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -95,7 +96,7 @@ const useOption = ( } } -const toCSV = (statisticAddressCounts?: State.StatisticAddressCount[]) => +const toCSV = (statisticAddressCounts?: ChartItem.AddressCount[]) => statisticAddressCounts ? statisticAddressCounts.map(data => [data.createdAtUnixtimestamp, data.addressesCount]) : [] export const AddressCountChart = ({ isThumbnail = false }: { isThumbnail?: boolean }) => { diff --git a/src/pages/StatisticsChart/activities/BalanceDistribution.tsx b/src/pages/StatisticsChart/activities/BalanceDistribution.tsx index 4b8cb04da..ee697141e 100644 --- a/src/pages/StatisticsChart/activities/BalanceDistribution.tsx +++ b/src/pages/StatisticsChart/activities/BalanceDistribution.tsx @@ -12,7 +12,8 @@ import { import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { localeNumberString } from '../../../utils/number' import { ChartCachedKeys } from '../../../constants/cache' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' +import { ChartColorConfig } from '../../../constants/common' const widthSpan = (value: string, currentLanguage: string) => tooltipWidth(value, currentLanguage === 'en' ? 270 : 110) @@ -26,8 +27,8 @@ const parseTooltip = ({ } const useOption = ( - statisticBalanceDistributions: State.StatisticBalanceDistribution[], - chartColor: State.ChartColor, + statisticBalanceDistributions: ChartItem.BalanceDistribution[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -165,20 +166,7 @@ const useOption = ( } } -const fetchStatisticBalanceDistributions = async () => { - const { addressBalanceDistribution } = await explorerService.api.fetchStatisticBalanceDistribution() - const balanceDistributions = addressBalanceDistribution.map(distribution => { - const [balance, addresses, sumAddresses] = distribution - return { - balance, - addresses, - sumAddresses, - } - }) - return balanceDistributions -} - -const toCSV = (statisticBalanceDistributions?: State.StatisticBalanceDistribution[]) => +const toCSV = (statisticBalanceDistributions?: ChartItem.BalanceDistribution[]) => statisticBalanceDistributions ? statisticBalanceDistributions.map((data, index) => [ `"${handleLogGroupAxis( @@ -197,7 +185,7 @@ export const BalanceDistributionChart = ({ isThumbnail = false }: { isThumbnail? title={t('statistic.balance_distribution')} description={t('statistic.balance_distribution_description')} isThumbnail={isThumbnail} - fetchData={fetchStatisticBalanceDistributions} + fetchData={explorerService.api.fetchStatisticBalanceDistribution} getEChartOption={useOption} toCSV={toCSV} cacheKey={ChartCachedKeys.BalanceDistribution} diff --git a/src/pages/StatisticsChart/activities/CellCount.tsx b/src/pages/StatisticsChart/activities/CellCount.tsx index ced46d668..0af7b0e08 100644 --- a/src/pages/StatisticsChart/activities/CellCount.tsx +++ b/src/pages/StatisticsChart/activities/CellCount.tsx @@ -11,7 +11,8 @@ import { import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { ChartCachedKeys } from '../../../constants/cache' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' +import { ChartColorConfig } from '../../../constants/common' const widthSpan = (value: string, currentLanguage: LanuageType) => tooltipWidth(value, currentLanguage === 'en' ? 125 : 80) @@ -43,8 +44,8 @@ const useTooltip = () => { } const useOption = ( - statisticCellCounts: State.StatisticCellCount[], - chartColor: State.ChartColor, + statisticCellCounts: ChartItem.CellCount[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, @@ -175,7 +176,7 @@ const useOption = ( } } -const toCSV = (statisticCellCounts: State.StatisticCellCount[]) => +const toCSV = (statisticCellCounts: ChartItem.CellCount[]) => statisticCellCounts ? statisticCellCounts.map(data => [ data.createdAtUnixtimestamp, diff --git a/src/pages/StatisticsChart/activities/TransactionCount.tsx b/src/pages/StatisticsChart/activities/TransactionCount.tsx index 357b76bc1..d2dc639ac 100644 --- a/src/pages/StatisticsChart/activities/TransactionCount.tsx +++ b/src/pages/StatisticsChart/activities/TransactionCount.tsx @@ -4,12 +4,13 @@ import { DATA_ZOOM_CONFIG, assertIsArray, handleAxis } from '../../../utils/char import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { ChartCachedKeys } from '../../../constants/cache' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' import { useCurrentLanguage } from '../../../utils/i18n' +import { ChartColorConfig } from '../../../constants/common' const useOption = ( - statisticTransactionCounts: State.StatisticTransactionCount[], - chartColor: State.ChartColor, + statisticTransactionCounts: ChartItem.TransactionCount[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, @@ -92,7 +93,7 @@ const useOption = ( } } -const toCSV = (statisticTransactionCounts: State.StatisticTransactionCount[]) => +const toCSV = (statisticTransactionCounts: ChartItem.TransactionCount[]) => statisticTransactionCounts ? statisticTransactionCounts.map(data => [data.createdAtUnixtimestamp, data.transactionsCount]) : [] diff --git a/src/pages/StatisticsChart/activities/TxFeeHistory.tsx b/src/pages/StatisticsChart/activities/TxFeeHistory.tsx index defd36968..bc57c545b 100644 --- a/src/pages/StatisticsChart/activities/TxFeeHistory.tsx +++ b/src/pages/StatisticsChart/activities/TxFeeHistory.tsx @@ -6,12 +6,13 @@ import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { shannonToCkbDecimal } from '../../../utils/util' import { isMainnet } from '../../../utils/chain' import { ChartCachedKeys } from '../../../constants/cache' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' import { useCurrentLanguage } from '../../../utils/i18n' +import { ChartColorConfig } from '../../../constants/common' const useOption = ( - statisticTxFeeHistories: State.StatisticTransactionFee[], - chartColor: State.ChartColor, + statisticTxFeeHistories: ChartItem.TransactionFee[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, @@ -97,7 +98,7 @@ const useOption = ( } } -const toCSV = (statisticTxFeeHistories: State.StatisticTransactionFee[]) => +const toCSV = (statisticTxFeeHistories: ChartItem.TransactionFee[]) => statisticTxFeeHistories ? statisticTxFeeHistories.map(data => [data.createdAtUnixtimestamp, shannonToCkbDecimal(data.totalTxFee, 8)]) : [] diff --git a/src/pages/StatisticsChart/block/AverageBlockTime.tsx b/src/pages/StatisticsChart/block/AverageBlockTime.tsx index da2676fe9..91cbca489 100644 --- a/src/pages/StatisticsChart/block/AverageBlockTime.tsx +++ b/src/pages/StatisticsChart/block/AverageBlockTime.tsx @@ -3,13 +3,14 @@ import { parseDateNoTime, parseSimpleDate, parseSimpleDateNoSecond } from '../.. import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { localeNumberString } from '../../../utils/number' import { DATA_ZOOM_CONFIG, assertIsArray, assertSerialsDataIsString, assertSerialsItem } from '../../../utils/chart' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' import { useCurrentLanguage } from '../../../utils/i18n' +import { ChartColorConfig } from '../../../constants/common' const useOption = ( - statisticAverageBlockTimes: State.StatisticAverageBlockTime[], - chartColor: State.ChartColor, + statisticAverageBlockTimes: ChartItem.AverageBlockTime[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -30,7 +31,7 @@ const useOption = ( containLabel: true, } - const maxAndMinAxis = (statisticAverageBlockTimes: State.StatisticAverageBlockTime[]) => { + const maxAndMinAxis = (statisticAverageBlockTimes: ChartItem.AverageBlockTime[]) => { const array = statisticAverageBlockTimes.flatMap(data => parseFloat(data.avgBlockTimeDaily)) return { max: Math.ceil(Math.max(...array) / 1000), @@ -178,7 +179,7 @@ const useOption = ( } } -const toCSV = (statisticAverageBlockTimes: State.StatisticAverageBlockTime[]) => +const toCSV = (statisticAverageBlockTimes: ChartItem.AverageBlockTime[]) => statisticAverageBlockTimes ? statisticAverageBlockTimes.map(data => [data.timestamp, data.avgBlockTimeDaily, data.avgBlockTimeWeekly]) : [] diff --git a/src/pages/StatisticsChart/block/BlockTimeDistribution.tsx b/src/pages/StatisticsChart/block/BlockTimeDistribution.tsx index 4f143ef2a..bb8ee2a11 100644 --- a/src/pages/StatisticsChart/block/BlockTimeDistribution.tsx +++ b/src/pages/StatisticsChart/block/BlockTimeDistribution.tsx @@ -2,12 +2,13 @@ import { useTranslation } from 'react-i18next' import { DATA_ZOOM_CONFIG, assertIsArray } from '../../../utils/chart' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { ChartCachedKeys } from '../../../constants/cache' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' import { useCurrentLanguage } from '../../../utils/i18n' +import { ChartColorConfig } from '../../../constants/common' const useOption = ( - statisticBlockTimeDistributions: State.StatisticBlockTimeDistribution[], - chartColor: State.ChartColor, + statisticBlockTimeDistributions: ChartItem.BlockTimeDistribution[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, @@ -89,29 +90,7 @@ const useOption = ( } } -const fetchStatisticBlockTimeDistributions = async () => { - const { blockTimeDistribution } = await explorerService.api.fetchStatisticBlockTimeDistribution() - const sumBlocks = blockTimeDistribution - .flatMap(data => Number(data[1])) - .reduce((previous, current) => previous + current) - const statisticBlockTimeDistributions = [ - { - time: '0', - ratio: '0', - }, - ].concat( - blockTimeDistribution.map(data => { - const [time, blocks] = data - return { - time, - ratio: (Number(blocks) / sumBlocks).toFixed(5), - } - }), - ) - return statisticBlockTimeDistributions -} - -const toCSV = (statisticBlockTimeDistributions: State.StatisticBlockTimeDistribution[]) => +const toCSV = (statisticBlockTimeDistributions: ChartItem.BlockTimeDistribution[]) => statisticBlockTimeDistributions ? statisticBlockTimeDistributions.map(data => [data.time, Number(data.ratio).toFixed(4)]) : [] @@ -123,7 +102,7 @@ export const BlockTimeDistributionChart = ({ isThumbnail = false }: { isThumbnai title={t('statistic.block_time_distribution_more')} description={t('statistic.block_time_distribution_description')} isThumbnail={isThumbnail} - fetchData={fetchStatisticBlockTimeDistributions} + fetchData={explorerService.api.fetchStatisticBlockTimeDistribution} getEChartOption={useOption} toCSV={toCSV} cacheKey={ChartCachedKeys.BlockTimeDistribution} diff --git a/src/pages/StatisticsChart/block/EpochTimeDistribution.tsx b/src/pages/StatisticsChart/block/EpochTimeDistribution.tsx index 0f347703f..ed3f85cea 100644 --- a/src/pages/StatisticsChart/block/EpochTimeDistribution.tsx +++ b/src/pages/StatisticsChart/block/EpochTimeDistribution.tsx @@ -3,13 +3,14 @@ import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { localeNumberString } from '../../../utils/number' import { parseHourFromMinute } from '../../../utils/date' import { DATA_ZOOM_CONFIG, assertIsArray } from '../../../utils/chart' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' import { useCurrentLanguage } from '../../../utils/i18n' +import { ChartColorConfig } from '../../../constants/common' const useOption = ( - statisticEpochTimeDistributions: State.StatisticEpochTimeDistribution[], - chartColor: State.ChartColor, + statisticEpochTimeDistributions: ChartItem.EpochTimeDistribution[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, @@ -96,19 +97,7 @@ const useOption = ( } } -const fetchStatisticEpochTimeDistributions = async () => { - const { epochTimeDistribution } = await explorerService.api.fetchStatisticEpochTimeDistribution() - const statisticEpochTimeDistributions: State.StatisticEpochTimeDistribution[] = epochTimeDistribution.map(data => { - const [time, epoch] = data - return { - time, - epoch, - } - }) - return statisticEpochTimeDistributions -} - -const toCSV = (statisticEpochTimeDistributions: State.StatisticEpochTimeDistribution[]) => +const toCSV = (statisticEpochTimeDistributions: ChartItem.EpochTimeDistribution[]) => statisticEpochTimeDistributions ? statisticEpochTimeDistributions.map(data => [parseHourFromMinute(data.time), data.epoch]) : [] @@ -120,7 +109,7 @@ export const EpochTimeDistributionChart = ({ isThumbnail = false }: { isThumbnai title={t('statistic.epoch_time_distribution_more')} description={t('statistic.epoch_time_distribution_description')} isThumbnail={isThumbnail} - fetchData={fetchStatisticEpochTimeDistributions} + fetchData={explorerService.api.fetchStatisticEpochTimeDistribution} getEChartOption={useOption} toCSV={toCSV} cacheKey={ChartCachedKeys.EpochTimeDistribution} diff --git a/src/pages/StatisticsChart/common/index.tsx b/src/pages/StatisticsChart/common/index.tsx index d20266b92..002b6e05c 100644 --- a/src/pages/StatisticsChart/common/index.tsx +++ b/src/pages/StatisticsChart/common/index.tsx @@ -23,7 +23,7 @@ import Content from '../../../components/Content' import { useChartQueryWithCache, useIsMobile, usePrevious, useWindowResize } from '../../../utils/hook' import { isDeepEqual } from '../../../utils/util' import { HelpTip } from '../../../components/HelpTip' -import { ChartColor } from '../../../constants/common' +import { ChartColor, ChartColorConfig } from '../../../constants/common' import { Response } from '../../../services/ExplorerService' const LoadingComp = ({ isThumbnail }: { isThumbnail?: boolean }) => (isThumbnail ? : ) @@ -165,7 +165,7 @@ export interface SmartChartPageProps { onFetched?: (dataList: T[]) => void getEChartOption: ( dataList: T[], - chartColor: State.ChartColor, + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail?: boolean, ) => echarts.EChartOption diff --git a/src/pages/StatisticsChart/mining/Difficulty.tsx b/src/pages/StatisticsChart/mining/Difficulty.tsx index 56b8aa720..894e64ee8 100644 --- a/src/pages/StatisticsChart/mining/Difficulty.tsx +++ b/src/pages/StatisticsChart/mining/Difficulty.tsx @@ -5,12 +5,13 @@ import { parseDateNoTime } from '../../../utils/date' import { handleDifficulty } from '../../../utils/number' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { ChartCachedKeys } from '../../../constants/cache' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' import { useCurrentLanguage } from '../../../utils/i18n' +import { ChartColorConfig } from '../../../constants/common' const useOption = ( - statisticDifficulties: State.StatisticDifficulty[], - chartColor: State.ChartColor, + statisticDifficulties: ChartItem.Difficulty[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -96,7 +97,7 @@ const useOption = ( } } -const toCSV = (statisticDifficulties: State.StatisticDifficulty[]) => +const toCSV = (statisticDifficulties: ChartItem.Difficulty[]) => statisticDifficulties ? statisticDifficulties.map(data => [data.createdAtUnixtimestamp, data.avgDifficulty]) : [] export const DifficultyChart = ({ isThumbnail = false }: { isThumbnail?: boolean }) => { diff --git a/src/pages/StatisticsChart/mining/DifficultyHashRate.tsx b/src/pages/StatisticsChart/mining/DifficultyHashRate.tsx index ecda3699e..f7e023cc1 100644 --- a/src/pages/StatisticsChart/mining/DifficultyHashRate.tsx +++ b/src/pages/StatisticsChart/mining/DifficultyHashRate.tsx @@ -9,13 +9,14 @@ import { } from '../../../utils/chart' import { handleDifficulty, handleHashRate } from '../../../utils/number' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' import { useCurrentLanguage } from '../../../utils/i18n' +import { ChartColorConfig } from '../../../constants/common' const useOption = ( - statisticDifficultyHashRates: State.StatisticDifficultyHashRate[], - chartColor: State.ChartColor, + statisticDifficultyHashRates: ChartItem.DifficultyHashRate[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -187,7 +188,7 @@ const useOption = ( } } -const toCSV = (statisticDifficultyHashRates: State.StatisticDifficultyHashRate[]) => +const toCSV = (statisticDifficultyHashRates: ChartItem.DifficultyHashRate[]) => statisticDifficultyHashRates ? statisticDifficultyHashRates.map(data => [data.epochNumber, data.difficulty, data.hashRate, data.uncleRate]) : [] diff --git a/src/pages/StatisticsChart/mining/DifficultyUncleRateEpoch.tsx b/src/pages/StatisticsChart/mining/DifficultyUncleRateEpoch.tsx index e44710a2b..5e842b1a6 100644 --- a/src/pages/StatisticsChart/mining/DifficultyUncleRateEpoch.tsx +++ b/src/pages/StatisticsChart/mining/DifficultyUncleRateEpoch.tsx @@ -5,8 +5,9 @@ import { assertSerialsDataIsString, assertIsArray, assertSerialsItem, handleAxis import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { parseHourFromMillisecond } from '../../../utils/date' import { ChartCachedKeys } from '../../../constants/cache' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' import { LanuageType, useCurrentLanguage } from '../../../utils/i18n' +import { ChartColorConfig } from '../../../constants/common' const widthSpan = (value: string, currentLanguage: LanuageType) => tooltipWidth(value, currentLanguage === 'en' ? 90 : 80) @@ -26,8 +27,8 @@ const useTooltip = () => { } const useOption = ( - statisticChartData: State.StatisticDifficultyUncleRateEpoch[], - chartColor: State.ChartColor, + statisticChartData: ChartItem.DifficultyUncleRateEpoch[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, @@ -202,7 +203,7 @@ const useOption = ( } } -const toCSV = (statisticDifficultyUncleRateEpochs: State.StatisticDifficultyUncleRateEpoch[]) => +const toCSV = (statisticDifficultyUncleRateEpochs: ChartItem.DifficultyUncleRateEpoch[]) => statisticDifficultyUncleRateEpochs ? statisticDifficultyUncleRateEpochs.map(data => [data.epochNumber, data.epochTime, data.epochLength]) : [] diff --git a/src/pages/StatisticsChart/mining/HashRate.tsx b/src/pages/StatisticsChart/mining/HashRate.tsx index 0ec49dbf3..19d0d367f 100644 --- a/src/pages/StatisticsChart/mining/HashRate.tsx +++ b/src/pages/StatisticsChart/mining/HashRate.tsx @@ -4,13 +4,14 @@ import { DATA_ZOOM_CONFIG, assertIsArray, handleAxis } from '../../../utils/char import { parseDateNoTime } from '../../../utils/date' import { handleHashRate } from '../../../utils/number' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' import { useCurrentLanguage } from '../../../utils/i18n' +import { ChartColorConfig } from '../../../constants/common' const useOption = ( - statisticHashRates: State.StatisticHashRate[], - chartColor: State.ChartColor, + statisticHashRates: ChartItem.HashRate[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, @@ -94,7 +95,7 @@ const useOption = ( } } -const toCSV = (statisticHashRates: State.StatisticHashRate[]) => +const toCSV = (statisticHashRates: ChartItem.HashRate[]) => statisticHashRates ? statisticHashRates.map(data => [data.createdAtUnixtimestamp, data.avgHashRate]) : [] export const HashRateChart = ({ isThumbnail = false }: { isThumbnail?: boolean }) => { diff --git a/src/pages/StatisticsChart/mining/MinerAddressDistribution.tsx b/src/pages/StatisticsChart/mining/MinerAddressDistribution.tsx index 8e33f2f91..f667d4d49 100644 --- a/src/pages/StatisticsChart/mining/MinerAddressDistribution.tsx +++ b/src/pages/StatisticsChart/mining/MinerAddressDistribution.tsx @@ -3,10 +3,11 @@ import { useHistory } from 'react-router' import { useTranslation } from 'react-i18next' import { tooltipColor, tooltipWidth, SmartChartPage, SmartChartPageProps } from '../common' import { ChartCachedKeys } from '../../../constants/cache' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' import { useAdaptMobileEllipsis, useAdaptPCEllipsis, useIsMobile } from '../../../utils/hook' import { useCurrentLanguage } from '../../../utils/i18n' import { assertNotArray } from '../../../utils/chart' +import { ChartColorConfig } from '../../../constants/common' const Colors = [ '#069ECD', @@ -25,8 +26,8 @@ const useOption = () => { const { t } = useTranslation() const currentLanguage = useCurrentLanguage() return ( - statisticMinerAddresses: State.StatisticMinerAddress[], - chartColor: State.ChartColor, + statisticMinerAddresses: ChartItem.MinerAddress[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, getAdaptAddressText: (address: string) => string, @@ -88,19 +89,7 @@ const useOption = () => { } } -const fetchStatisticMinerAddresses = async () => { - const { minerAddressDistribution } = await explorerService.api.fetchStatisticMinerAddressDistribution() - const blockSum = Object.values(minerAddressDistribution).reduce((sum, val) => sum + Number(val), 0) - const statisticMinerAddresses: State.StatisticMinerAddress[] = Object.entries(minerAddressDistribution).map( - ([key, val]) => ({ - address: key, - radio: (Number(val) / blockSum).toFixed(3), - }), - ) - return statisticMinerAddresses -} - -const toCSV = (statisticMinerAddresses: State.StatisticMinerAddress[]) => +const toCSV = (statisticMinerAddresses: ChartItem.MinerAddress[]) => statisticMinerAddresses ? statisticMinerAddresses.map(data => [data.address, data.radio]) : [] export const MinerAddressDistributionChart = ({ isThumbnail = false }: { isThumbnail?: boolean }) => { @@ -120,7 +109,7 @@ export const MinerAddressDistributionChart = ({ isThumbnail = false }: { isThumb const adaptMobileEllipsis = useAdaptMobileEllipsis() const adaptPCEllipsis = useAdaptPCEllipsis(80) const parseOption = useOption() - const getEChartOption: SmartChartPageProps['getEChartOption'] = useCallback( + const getEChartOption: SmartChartPageProps['getEChartOption'] = useCallback( (...args) => parseOption(...args, address => (isMobile ? adaptMobileEllipsis(address, 4) : adaptPCEllipsis(address, 2))), [adaptMobileEllipsis, adaptPCEllipsis, isMobile, parseOption], @@ -131,7 +120,7 @@ export const MinerAddressDistributionChart = ({ isThumbnail = false }: { isThumb title={t('statistic.miner_addresses_rank')} isThumbnail={isThumbnail} chartProps={{ onClick: !isThumbnail ? onClick : undefined }} - fetchData={fetchStatisticMinerAddresses} + fetchData={explorerService.api.fetchStatisticMinerAddressDistribution} getEChartOption={getEChartOption} toCSV={toCSV} cacheKey={ChartCachedKeys.MinerAddressDistribution} diff --git a/src/pages/StatisticsChart/mining/MinerVersionDistribution.tsx b/src/pages/StatisticsChart/mining/MinerVersionDistribution.tsx index 7cc120d78..b49b00cbe 100644 --- a/src/pages/StatisticsChart/mining/MinerVersionDistribution.tsx +++ b/src/pages/StatisticsChart/mining/MinerVersionDistribution.tsx @@ -4,6 +4,7 @@ import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { ChartCachedKeys } from '../../../constants/cache' import { explorerService } from '../../../services/ExplorerService' import { useCurrentLanguage } from '../../../utils/i18n' +import { ChartColorConfig } from '../../../constants/common' const Colors = [ '#069ECD', @@ -24,8 +25,8 @@ interface VersionRecord { } const useOption = ( - list: Array, - chartColor: State.ChartColor, + list: VersionRecord[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -109,7 +110,7 @@ const fetchData = async () => { })) } -const toCSV = (versionList: Array) => versionList?.map(r => [r.version, `${r.percent}%`]) ?? [] +const toCSV = (versionList: VersionRecord[]) => versionList?.map(r => [r.version, `${r.percent}%`]) ?? [] export const MinerVersionDistributionChart = ({ isThumbnail = false }: { isThumbnail?: boolean }) => { const [t] = useTranslation() diff --git a/src/pages/StatisticsChart/mining/UncleRate.tsx b/src/pages/StatisticsChart/mining/UncleRate.tsx index 4457cfee1..384910617 100644 --- a/src/pages/StatisticsChart/mining/UncleRate.tsx +++ b/src/pages/StatisticsChart/mining/UncleRate.tsx @@ -2,18 +2,19 @@ import { useTranslation } from 'react-i18next' import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { DATA_ZOOM_CONFIG, assertIsArray } from '../../../utils/chart' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' import { useCurrentLanguage } from '../../../utils/i18n' +import { ChartColorConfig } from '../../../constants/common' -const max = (statisticUncleRates: State.StatisticUncleRate[]) => { +const max = (statisticUncleRates: ChartItem.UncleRate[]) => { const array = statisticUncleRates.flatMap(data => Number(data.uncleRate) * 100) return Math.max(5, Math.ceil(Math.max(...array))) } const useOption = ( - statisticUncleRates: State.StatisticUncleRate[], - chartColor: State.ChartColor, + statisticUncleRates: ChartItem.UncleRate[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, @@ -110,7 +111,7 @@ const useOption = ( } } -const toCSV = (statisticUncleRates: State.StatisticUncleRate[]) => +const toCSV = (statisticUncleRates: ChartItem.UncleRate[]) => statisticUncleRates ? statisticUncleRates.map(data => [data.createdAtUnixtimestamp, data.uncleRate]) : [] export const UncleRateChart = ({ isThumbnail = false }: { isThumbnail?: boolean }) => { diff --git a/src/pages/StatisticsChart/monetary/AnnualPercentageCompensation.tsx b/src/pages/StatisticsChart/monetary/AnnualPercentageCompensation.tsx index 2481af0e2..2a86d93d2 100644 --- a/src/pages/StatisticsChart/monetary/AnnualPercentageCompensation.tsx +++ b/src/pages/StatisticsChart/monetary/AnnualPercentageCompensation.tsx @@ -3,11 +3,12 @@ import { useCurrentLanguage } from '../../../utils/i18n' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { DATA_ZOOM_CONFIG, assertIsArray } from '../../../utils/chart' import { ChartCachedKeys } from '../../../constants/cache' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' +import { ChartColorConfig } from '../../../constants/common' const useOption = ( - statisticAnnualPercentageCompensations: State.StatisticAnnualPercentageCompensation[], - chartColor: State.ChartColor, + statisticAnnualPercentageCompensations: ChartItem.AnnualPercentageCompensation[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, @@ -93,18 +94,7 @@ const useOption = ( } } -const fetchStatisticAnnualPercentageCompensations = async () => { - const { nominalApc } = await explorerService.api.fetchStatisticAnnualPercentageCompensation() - const statisticAnnualPercentageCompensations = nominalApc - .filter((_apc, index) => index % 3 === 0 || index === nominalApc.length - 1) - .map((apc, index) => ({ - year: 0.25 * index, - apc, - })) - return statisticAnnualPercentageCompensations -} - -const toCSV = (statisticAnnualPercentageCompensations: State.StatisticAnnualPercentageCompensation[]) => +const toCSV = (statisticAnnualPercentageCompensations: ChartItem.AnnualPercentageCompensation[]) => statisticAnnualPercentageCompensations ? statisticAnnualPercentageCompensations.map(data => [data.year, (Number(data.apc) / 100).toFixed(4)]) : [] @@ -116,7 +106,7 @@ export const AnnualPercentageCompensationChart = ({ isThumbnail = false }: { isT title={t('statistic.nominal_apc')} description={t('statistic.nominal_rpc_description')} isThumbnail={isThumbnail} - fetchData={fetchStatisticAnnualPercentageCompensations} + fetchData={explorerService.api.fetchStatisticAnnualPercentageCompensation} getEChartOption={useOption} toCSV={toCSV} cacheKey={ChartCachedKeys.APC} diff --git a/src/pages/StatisticsChart/monetary/InflationRate.tsx b/src/pages/StatisticsChart/monetary/InflationRate.tsx index 50542b0ea..8e60d1a4f 100644 --- a/src/pages/StatisticsChart/monetary/InflationRate.tsx +++ b/src/pages/StatisticsChart/monetary/InflationRate.tsx @@ -3,11 +3,12 @@ import { useCurrentLanguage } from '../../../utils/i18n' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { DATA_ZOOM_CONFIG, assertSerialsDataIsString, assertIsArray, assertSerialsItem } from '../../../utils/chart' import { ChartCachedKeys } from '../../../constants/cache' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' +import { ChartColorConfig } from '../../../constants/common' const useOption = ( - statisticInflationRates: State.StatisticInflationRate[], - chartColor: State.ChartColor, + statisticInflationRates: ChartItem.InflationRate[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -147,24 +148,7 @@ const useOption = ( } } -const fetchStatisticInflationRates = async () => { - const { nominalApc, nominalInflationRate, realInflationRate } = - await explorerService.api.fetchStatisticInflationRate() - const statisticInflationRates = [] - for (let i = 0; i < nominalApc.length; i++) { - if (i % 6 === 0 || i === nominalApc.length - 1) { - statisticInflationRates.push({ - year: i % 6 === 0 ? Math.floor(i / 6) * 0.5 : 50, - nominalApc: nominalApc[i], - nominalInflationRate: nominalInflationRate[i], - realInflationRate: realInflationRate[i], - }) - } - } - return statisticInflationRates -} - -const toCSV = (statisticInflationRates: State.StatisticInflationRate[]) => +const toCSV = (statisticInflationRates: ChartItem.InflationRate[]) => statisticInflationRates ? statisticInflationRates.map(data => [ data.year, @@ -181,7 +165,7 @@ export const InflationRateChart = ({ isThumbnail = false }: { isThumbnail?: bool title={t('statistic.inflation_rate')} description={t('statistic.inflation_rate_description')} isThumbnail={isThumbnail} - fetchData={fetchStatisticInflationRates} + fetchData={explorerService.api.fetchStatisticInflationRate} getEChartOption={useOption} toCSV={toCSV} cacheKey={ChartCachedKeys.InflationRate} diff --git a/src/pages/StatisticsChart/monetary/Liquidity.tsx b/src/pages/StatisticsChart/monetary/Liquidity.tsx index a5de0a39b..c282efb05 100644 --- a/src/pages/StatisticsChart/monetary/Liquidity.tsx +++ b/src/pages/StatisticsChart/monetary/Liquidity.tsx @@ -11,11 +11,12 @@ import { } from '../../../utils/chart' import { shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' import { ChartCachedKeys } from '../../../constants/cache' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' +import { ChartColorConfig } from '../../../constants/common' const useOption = ( - statisticLiquidity: State.StatisticLiquidity[], - chartColor: State.ChartColor, + statisticLiquidity: ChartItem.Liquidity[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, @@ -178,7 +179,7 @@ const useOption = ( } } -const toCSV = (statisticLiquidity: State.StatisticLiquidity[]) => +const toCSV = (statisticLiquidity: ChartItem.Liquidity[]) => statisticLiquidity ? statisticLiquidity.map(data => [ data.createdAtUnixtimestamp, diff --git a/src/pages/StatisticsChart/monetary/SecondaryIssuance.tsx b/src/pages/StatisticsChart/monetary/SecondaryIssuance.tsx index b5e9f890c..2915579ee 100644 --- a/src/pages/StatisticsChart/monetary/SecondaryIssuance.tsx +++ b/src/pages/StatisticsChart/monetary/SecondaryIssuance.tsx @@ -9,7 +9,8 @@ import { assertSerialsItem, } from '../../../utils/chart' import { ChartCachedKeys } from '../../../constants/cache' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' +import { ChartColorConfig } from '../../../constants/common' const widthSpan = (value: string, currentLanguage: LanuageType) => tooltipWidth(value, currentLanguage === 'en' ? 155 : 70) @@ -35,8 +36,8 @@ const useTooltip = () => { } const useOption = ( - statisticSecondaryIssuance: State.StatisticSecondaryIssuance[], - chartColor: State.ChartColor, + statisticSecondaryIssuance: ChartItem.SecondaryIssuance[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, @@ -171,7 +172,7 @@ const useOption = ( } } -const toCSV = (statisticSecondaryIssuance: State.StatisticSecondaryIssuance[]) => +const toCSV = (statisticSecondaryIssuance: ChartItem.SecondaryIssuance[]) => statisticSecondaryIssuance ? statisticSecondaryIssuance.map(data => [ data.createdAtUnixtimestamp, diff --git a/src/pages/StatisticsChart/monetary/TotalSupply.tsx b/src/pages/StatisticsChart/monetary/TotalSupply.tsx index 15dde1706..16a6adb72 100644 --- a/src/pages/StatisticsChart/monetary/TotalSupply.tsx +++ b/src/pages/StatisticsChart/monetary/TotalSupply.tsx @@ -12,7 +12,8 @@ import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' import { ChartCachedKeys } from '../../../constants/cache' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' +import { ChartColorConfig } from '../../../constants/common' const widthSpan = (value: string, currentLanguage: LanuageType) => tooltipWidth(value, currentLanguage === 'en' ? 125 : 80) @@ -45,8 +46,8 @@ const useTooltip = () => { } const useOption = ( - statisticTotalSupplies: State.StatisticTotalSupply[], - chartColor: State.ChartColor, + statisticTotalSupplies: ChartItem.TotalSupply[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, @@ -187,7 +188,7 @@ const useOption = ( } } -const toCSV = (statisticTotalSupplies: State.StatisticTotalSupply[]) => +const toCSV = (statisticTotalSupplies: ChartItem.TotalSupply[]) => statisticTotalSupplies ? statisticTotalSupplies.map(data => [ data.createdAtUnixtimestamp, diff --git a/src/pages/StatisticsChart/nervosDao/CirculationRatio.tsx b/src/pages/StatisticsChart/nervosDao/CirculationRatio.tsx index 51d812a73..a423ee114 100644 --- a/src/pages/StatisticsChart/nervosDao/CirculationRatio.tsx +++ b/src/pages/StatisticsChart/nervosDao/CirculationRatio.tsx @@ -4,11 +4,12 @@ import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { DATA_ZOOM_CONFIG, assertIsArray } from '../../../utils/chart' import { ChartCachedKeys } from '../../../constants/cache' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' +import { ChartColorConfig } from '../../../constants/common' const useOption = ( - statisticCirculationRatios: State.StatisticCirculationRatio[], - chartColor: State.ChartColor, + statisticCirculationRatios: ChartItem.CirculationRatio[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -94,7 +95,7 @@ const useOption = ( } } -const toCSV = (statisticCirculationRatios: State.StatisticCirculationRatio[]) => +const toCSV = (statisticCirculationRatios: ChartItem.CirculationRatio[]) => statisticCirculationRatios ? statisticCirculationRatios.map(data => [data.createdAtUnixtimestamp, data.circulationRatio]) : [] diff --git a/src/pages/StatisticsChart/nervosDao/NewDaoDeposit.tsx b/src/pages/StatisticsChart/nervosDao/NewDaoDeposit.tsx index 3baf2c035..d129676fb 100644 --- a/src/pages/StatisticsChart/nervosDao/NewDaoDeposit.tsx +++ b/src/pages/StatisticsChart/nervosDao/NewDaoDeposit.tsx @@ -13,7 +13,8 @@ import { shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' import { isMainnet } from '../../../utils/chain' import { tooltipWidth, tooltipColor, SeriesItem, SmartChartPage } from '../common' import { ChartCachedKeys } from '../../../constants/cache' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' +import { ChartColorConfig } from '../../../constants/common' const widthSpan = (value: string, language: LanuageType) => tooltipWidth(value, language === 'en' ? 140 : 120) @@ -39,8 +40,8 @@ const useTooltip = () => { } const useOption = ( - statisticNewDaoDeposits: State.StatisticNewDaoDeposit[], - chartColor: State.ChartColor, + statisticNewDaoDeposits: ChartItem.NewDaoDeposit[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -177,7 +178,7 @@ const useOption = ( } } -const toCSV = (statisticNewDaoDeposits: State.StatisticNewDaoDeposit[]) => +const toCSV = (statisticNewDaoDeposits: ChartItem.NewDaoDeposit[]) => statisticNewDaoDeposits ? statisticNewDaoDeposits.map(data => [ data.createdAtUnixtimestamp, diff --git a/src/pages/StatisticsChart/nervosDao/TotalDaoDeposit.tsx b/src/pages/StatisticsChart/nervosDao/TotalDaoDeposit.tsx index eb0cd5f07..5d15a3ec0 100644 --- a/src/pages/StatisticsChart/nervosDao/TotalDaoDeposit.tsx +++ b/src/pages/StatisticsChart/nervosDao/TotalDaoDeposit.tsx @@ -13,7 +13,8 @@ import { shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' import { isMainnet } from '../../../utils/chain' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { ChartCachedKeys } from '../../../constants/cache' -import { explorerService } from '../../../services/ExplorerService' +import { ChartItem, explorerService } from '../../../services/ExplorerService' +import { ChartColorConfig } from '../../../constants/common' const widthSpan = (value: string, language: LanuageType) => tooltipWidth(value, language === 'en' ? 168 : 110) @@ -38,8 +39,8 @@ const useTooltip = () => { } const useOption = ( - statisticTotalDaoDeposits: State.StatisticTotalDaoDeposit[], - chartColor: State.ChartColor, + statisticTotalDaoDeposits: ChartItem.TotalDaoDeposit[], + chartColor: ChartColorConfig, isMobile: boolean, isThumbnail = false, @@ -175,7 +176,7 @@ const useOption = ( } } -const toCSV = (statisticTotalDaoDeposits: State.StatisticTotalDaoDeposit[]) => +const toCSV = (statisticTotalDaoDeposits: ChartItem.TotalDaoDeposit[]) => statisticTotalDaoDeposits ? statisticTotalDaoDeposits.map(data => [ data.createdAtUnixtimestamp, diff --git a/src/pages/Tokens/index.tsx b/src/pages/Tokens/index.tsx index 6476ac738..e40ed673d 100644 --- a/src/pages/Tokens/index.tsx +++ b/src/pages/Tokens/index.tsx @@ -26,8 +26,9 @@ import styles from './styles.module.scss' import { useIsMobile, usePaginationParamsInPage } from '../../utils/hook' import { explorerService } from '../../services/ExplorerService' import { QueryResult } from '../../components/QueryResult' +import { UDT } from '../../models/UDT' -const TokenItem = ({ token, isLast }: { token: State.UDT; isLast?: boolean }) => { +const TokenItem = ({ token, isLast }: { token: UDT; isLast?: boolean }) => { const { displayName, fullName, uan } = token const { t } = useTranslation() diff --git a/src/pages/Transaction/TransactionCell/index.tsx b/src/pages/Transaction/TransactionCell/index.tsx index a85676598..735516ebc 100644 --- a/src/pages/Transaction/TransactionCell/index.tsx +++ b/src/pages/Transaction/TransactionCell/index.tsx @@ -42,6 +42,7 @@ import { useDeprecatedAddr, useIsMobile, useNewAddr } from '../../../utils/hook' import { useDASAccount } from '../../../contexts/providers/dasQuery' import styles from './styles.module.scss' import AddressText from '../../../components/AddressText' +import { Cell } from '../../../models/Cell' export const Addr: FC<{ address: string; isCellBase: boolean }> = ({ address, isCellBase }) => { const alias = useDASAccount(address) @@ -86,7 +87,7 @@ const TransactionCellIndexAddress = ({ index, isAddrNew, }: { - cell: State.Cell + cell: Cell cellType: CellType index: number isAddrNew: boolean @@ -139,7 +140,7 @@ const TransactionCellIndexAddress = ({ ) } -const useParseNftInfo = (cell: State.Cell) => { +const useParseNftInfo = (cell: Cell) => { const { t } = useTranslation() if (cell.cellType === 'nrc_721_token') { const nftInfo = cell.extraInfo @@ -170,7 +171,7 @@ const useParseNftInfo = (cell: State.Cell) => { } } -const TransactionCellDetail = ({ cell }: { cell: State.Cell }) => { +const TransactionCellDetail = ({ cell }: { cell: Cell }) => { const { t } = useTranslation() let detailTitle = t('transaction.ckb_capacity') let detailIcon @@ -253,7 +254,7 @@ const TransactionCellDetail = ({ cell }: { cell: State.Cell }) => { ) } -const TransactionCellInfo = ({ cell, children }: { cell: State.Cell; children: string | ReactNode }) => { +const TransactionCellInfo = ({ cell, children }: { cell: Cell; children: string | ReactNode }) => { const [showModal, setShowModal] = useState(false) return ( @@ -275,7 +276,7 @@ const TransactionCellInfo = ({ cell, children }: { cell: State.Cell; children: s ) } -const TransactionCellCapacityAmount = ({ cell }: { cell: State.Cell }) => { +const TransactionCellCapacityAmount = ({ cell }: { cell: Cell }) => { const { t } = useTranslation() if (cell.cellType === 'udt') { const udtInfo = cell.extraInfo @@ -302,7 +303,7 @@ export default ({ showReward, isAddrNew, }: { - cell: State.Cell + cell: Cell cellType: CellType index: number txHash?: string diff --git a/src/pages/Transaction/TransactionCell/styled.tsx b/src/pages/Transaction/TransactionCell/styled.tsx index d1e2aa071..4940b13f8 100644 --- a/src/pages/Transaction/TransactionCell/styled.tsx +++ b/src/pages/Transaction/TransactionCell/styled.tsx @@ -98,10 +98,9 @@ export const TransactionCellAddressPanel = styled.div` } ` -export const TransactionCellHashPanel = styled.div` - color: ${({ highLight = false, theme }: { highLight?: boolean; theme: State.Theme }) => - highLight ? `${theme.primary}` : '#000000'}; - text-align: ${({ highLight = false }: { highLight?: boolean }) => (highLight ? 'left' : 'center')}; +export const TransactionCellHashPanel = styled.div<{ highLight?: boolean }>` + color: ${({ highLight = false, theme }) => (highLight ? `${theme.primary}` : '#000000')}; + text-align: ${({ highLight = false }) => (highLight ? 'left' : 'center')}; flex: 1; min-width: 0; display: flex; diff --git a/src/pages/Transaction/TransactionCellList/index.tsx b/src/pages/Transaction/TransactionCellList/index.tsx index df6e0a4e5..943b1aac2 100644 --- a/src/pages/Transaction/TransactionCellList/index.tsx +++ b/src/pages/Transaction/TransactionCellList/index.tsx @@ -9,6 +9,7 @@ import { ReactComponent as DeprecatedAddrOn } from '../../../assets/deprecated_a import { ReactComponent as DeprecatedAddrOff } from '../../../assets/deprecated_addr_off.svg' import { ReactComponent as Warning } from '../../../assets/warning.svg' import styles from './styles.module.scss' +import { Cell } from '../../../models/Cell' const SCROLL_BOTTOM_OFFSET = 5 const SCROLL_LOADING_TIME = 400 @@ -20,8 +21,8 @@ export default ({ showReward, addrToggle: { isAddrNew, setIsAddrNew }, }: { - inputs?: State.Cell[] - outputs?: State.Cell[] + inputs?: Cell[] + outputs?: Cell[] txHash?: string showReward?: boolean addrToggle: { diff --git a/src/pages/Transaction/TransactionCellScript/index.tsx b/src/pages/Transaction/TransactionCellScript/index.tsx index 8ee2185cd..4f3960ccf 100644 --- a/src/pages/Transaction/TransactionCellScript/index.tsx +++ b/src/pages/Transaction/TransactionCellScript/index.tsx @@ -28,6 +28,7 @@ import { HelpTip } from '../../../components/HelpTip' import { useSetToast } from '../../../components/Toast' import { CellBasicInfo } from '../../../utils/transformer' import { isAxiosError } from '../../../utils/error' +import { Script } from '../../../models/Script' enum CellInfo { LOCK = 1, @@ -42,9 +43,9 @@ interface CellData { data: string } -type CellInfoValue = State.Script | CellData | CapacityUsage | null | undefined +type CellInfoValue = Script | CellData | CapacityUsage | null | undefined -function isScript(content: CellInfoValue): content is State.Script { +function isScript(content: CellInfoValue): content is Script { return content != null && 'codeHash' in content } diff --git a/src/pages/Transaction/TransactionCellScript/styled.tsx b/src/pages/Transaction/TransactionCellScript/styled.tsx index 7aec2f317..495ccdc4a 100644 --- a/src/pages/Transaction/TransactionCellScript/styled.tsx +++ b/src/pages/Transaction/TransactionCellScript/styled.tsx @@ -9,12 +9,12 @@ export const TransactionDetailContainer = styled.div` } ` -export const TransactionDetailItem = styled.div` +export const TransactionDetailItem = styled.div<{ selected?: boolean }>` cursor: pointer; position: relative; display: flex; padding-bottom: 22px; - color: ${(props: { selected?: boolean }) => (props.selected ? '#000000' : 'rgba(0, 0, 0, 0.6)')}; + color: ${props => (props.selected ? '#000000' : 'rgba(0, 0, 0, 0.6)')}; font-weight: 600; font-size: 16px; align-items: center; @@ -28,10 +28,10 @@ export const TransactionDetailItem = styled.div` left: 2px; bottom: 0; content: ''; - background: ${(props: { theme: State.Theme }) => `${props.theme.primary}`}; + background: ${props => `${props.theme.primary}`}; width: calc(100% - 4px); height: 5px; - display: ${(props: { theme: State.Theme; selected: boolean }) => (props.selected ? 'block' : 'none')}; + display: ${props => (props.selected ? 'block' : 'none')}; } ` @@ -180,7 +180,6 @@ export const TransactionDetailCopyButton = styled.div` cursor: pointer; width: 150px; height: 40px; - background: ${props => props.theme.default}; border: 1px ${props => props.theme.primary} solid; border-radius: 6px; display: flex; diff --git a/src/pages/Transaction/TransactionComp/TransactionComp.tsx b/src/pages/Transaction/TransactionComp/TransactionComp.tsx index 85a578974..d36f8dd1c 100644 --- a/src/pages/Transaction/TransactionComp/TransactionComp.tsx +++ b/src/pages/Transaction/TransactionComp/TransactionComp.tsx @@ -1,7 +1,9 @@ import TransactionCellList from '../TransactionCellList' import { useAddrFormatToggle } from '../../../utils/hook' +import { Cell } from '../../../models/Cell' +import { Transaction } from '../../../models/Transaction' -const handleCellbaseInputs = (inputs: State.Cell[], outputs: State.Cell[]) => { +const handleCellbaseInputs = (inputs: Cell[], outputs: Cell[]) => { if (inputs[0] && inputs[0].fromCellbase && outputs[0] && outputs[0].baseReward) { const resultInputs = inputs resultInputs[0] = { @@ -16,7 +18,7 @@ const handleCellbaseInputs = (inputs: State.Cell[], outputs: State.Cell[]) => { return inputs } -export const TransactionComp = ({ transaction }: { transaction: State.Transaction }) => { +export const TransactionComp = ({ transaction }: { transaction: Transaction }) => { const { transactionHash, displayInputs, displayOutputs, blockNumber, isCellbase } = transaction const { isNew: isAddrNew, setIsNew: setIsAddrNew } = useAddrFormatToggle() diff --git a/src/pages/Transaction/TransactionComp/TransactionLite/TransactionBadge.tsx b/src/pages/Transaction/TransactionComp/TransactionLite/TransactionBadge.tsx index 0eec5ecd6..485350c77 100644 --- a/src/pages/Transaction/TransactionComp/TransactionLite/TransactionBadge.tsx +++ b/src/pages/Transaction/TransactionComp/TransactionLite/TransactionBadge.tsx @@ -1,11 +1,12 @@ import { Tooltip } from 'antd' import styles from './TransactionBadge.module.scss' +import { Cell } from '../../../../models/Cell' type Props = { - cellType: State.CellType + cellType: Cell['cellType'] capacity?: string } -const cellTypeDisplayMap: Record = { +const cellTypeDisplayMap: Record = { normal: '', udt: '', nervos_dao_deposit: 'Nervos DAO Deposit', @@ -19,6 +20,9 @@ const cellTypeDisplayMap: Record = { m_nft_token: '', nrc_721_token: '', nrc_721_factory: '', + nft_transfer: '', + simple_transfer: '', + nft_mint: '', } export const TransactionBadge = ({ cellType, capacity }: Props) => { diff --git a/src/pages/Transaction/TransactionComp/TransactionLite/TransactionLite.tsx b/src/pages/Transaction/TransactionComp/TransactionLite/TransactionLite.tsx index 6488c43fe..0a437f7d8 100644 --- a/src/pages/Transaction/TransactionComp/TransactionLite/TransactionLite.tsx +++ b/src/pages/Transaction/TransactionComp/TransactionLite/TransactionLite.tsx @@ -10,10 +10,10 @@ import { shannonToCkb } from '../../../../utils/util' import { Addr } from '../../TransactionCell' import { defaultTransactionLiteDetails } from '../../state' import { TransactionBadge } from './TransactionBadge' -import { explorerService } from '../../../../services/ExplorerService' +import { TransactionRecord, TransactionRecordTransfer, explorerService } from '../../../../services/ExplorerService' import { useIsMobile } from '../../../../utils/hook' -const getTransferItemTag = (transfer: State.LiteTransfer) => { +const getTransferItemTag = (transfer: TransactionRecordTransfer) => { const { cellType, udtInfo, mNftInfo } = transfer if (cellType === 'm_nft_token' || cellType === 'm_nft_class' || cellType === 'm_nft_issuer') { return `NFT-${mNftInfo?.className ?? 'Unknown'}` @@ -44,7 +44,7 @@ export const TransactionCompLite: FC<{ isCellbase: boolean }> = ({ isCellbase }) const ckbTransactionDetails = await explorerService.api.fetchTransactionLiteDetailsByHash(txHash) return ckbTransactionDetails.data }) - const transactionLiteDetails: State.TransactionLiteDetails[] = query.data ?? defaultTransactionLiteDetails + const transactionLiteDetails: TransactionRecord[] = query.data ?? defaultTransactionLiteDetails return ( <> {transactionLiteDetails && @@ -64,7 +64,7 @@ export const TransactionCompLite: FC<{ isCellbase: boolean }> = ({ isCellbase }) ) } -export const DesktopTransferItems = (props: { details: State.TransactionLiteDetails }) => { +export const DesktopTransferItems = (props: { details: TransactionRecord }) => { const { details } = props const { transfers } = details return ( @@ -84,7 +84,7 @@ export const DesktopTransferItems = (props: { details: State.TransactionLiteDeta ) } -export const MobileTransferItems = (props: { details: State.TransactionLiteDetails }) => { +export const MobileTransferItems = (props: { details: TransactionRecord }) => { const { details } = props const { transfers } = details return ( @@ -110,7 +110,7 @@ export const MobileTransferItems = (props: { details: State.TransactionLiteDetai ) } -const TransferAmount: FC<{ transfer: State.LiteTransfer }> = ({ transfer }) => { +const TransferAmount: FC<{ transfer: TransactionRecordTransfer }> = ({ transfer }) => { const isUdt = transfer.cellType === 'udt' const isNft = transfer.cellType === 'm_nft_token' diff --git a/src/pages/Transaction/TransactionComp/TransactionOverview.tsx b/src/pages/Transaction/TransactionComp/TransactionOverview.tsx index 67f4a5821..33763d582 100644 --- a/src/pages/Transaction/TransactionComp/TransactionOverview.tsx +++ b/src/pages/Transaction/TransactionComp/TransactionOverview.tsx @@ -26,6 +26,7 @@ import { TransactionInfoItemPanel, } from './styled' import { useLatestBlockNumber } from '../../../services/ExplorerService' +import { Transaction } from '../../../models/Transaction' const showTxStatus = (txStatus: string) => txStatus?.replace(/^\S/, s => s.toUpperCase()) ?? '-' const TransactionBlockHeight = ({ blockNumber, txStatus }: { blockNumber: number; txStatus: string }) => ( @@ -104,7 +105,7 @@ const TransactionInfoItemWrapper = ({ ) -export const TransactionOverview: FC<{ transaction: State.Transaction; layout: LayoutLiteProfessional }> = ({ +export const TransactionOverview: FC<{ transaction: Transaction; layout: LayoutLiteProfessional }> = ({ transaction, layout, }) => { @@ -227,7 +228,7 @@ export const TransactionOverview: FC<{ transaction: State.Transaction; layout: L title: t('transaction.cycles'), content: liteTxCyclesDataContent, } - const overviewItems: Array = [] + const overviewItems: OverviewItemData[] = [] if (txStatus === 'committed') { overviewItems.push(blockHeightData, timestampData) if (confirmation >= 0) { diff --git a/src/pages/Transaction/TransactionReward/index.tsx b/src/pages/Transaction/TransactionReward/index.tsx index aa4c77e18..d05cce57b 100644 --- a/src/pages/Transaction/TransactionReward/index.tsx +++ b/src/pages/Transaction/TransactionReward/index.tsx @@ -4,8 +4,9 @@ import { localeNumberString } from '../../../utils/number' import DecimalCapacity from '../../../components/DecimalCapacity' import { RewardPenal, RewardItemPenal } from './styled' import { useIsMobile } from '../../../utils/hook' +import { Cell } from '../../../models/Cell' -const useRewards = (cell: State.Cell, isMobile: boolean) => { +const useRewards = (cell: Cell, isMobile: boolean) => { const { t } = useTranslation() return [ { @@ -27,7 +28,7 @@ const useRewards = (cell: State.Cell, isMobile: boolean) => { ] } -const TransactionReward = ({ cell, showReward }: { cell: State.Cell; showReward?: boolean }) => { +const TransactionReward = ({ cell, showReward }: { cell: Cell; showReward?: boolean }) => { const isMobile = useIsMobile() const { t } = useTranslation() // [0, 11] block doesn't show block reward and only cellbase show block reward diff --git a/src/pages/Transaction/state.ts b/src/pages/Transaction/state.ts index b00f3d128..9e8857770 100644 --- a/src/pages/Transaction/state.ts +++ b/src/pages/Transaction/state.ts @@ -1,4 +1,7 @@ -export const defaultTransactionInfo: State.Transaction = { +import { Transaction } from '../../models/Transaction' +import { TransactionRecord } from '../../services/ExplorerService' + +export const defaultTransactionInfo: Transaction = { transactionHash: '', blockNumber: 0, blockTimestamp: 0, @@ -24,7 +27,7 @@ export const defaultTransactionInfo: State.Transaction = { maxCycles: null, } -export const defaultTransactionLiteDetails: State.TransactionLiteDetails[] = [ +export const defaultTransactionLiteDetails: TransactionRecord[] = [ { address: '', transfers: [], diff --git a/src/pages/TransactionList/index.tsx b/src/pages/TransactionList/index.tsx index 520be2039..9da30ab34 100644 --- a/src/pages/TransactionList/index.tsx +++ b/src/pages/TransactionList/index.tsx @@ -20,6 +20,7 @@ import { RouteState } from '../../routes/state' import { assert } from '../../utils/error' import { ReactComponent as SortIcon } from '../../assets/sort_icon.svg' import { TableTitleRowItem } from '../../components/Table/styled' +import { Transaction } from '../../models/Transaction' type TxStatus = 'confirmed' | 'pending' @@ -31,12 +32,12 @@ interface SortItemCardData extends ItemCardData { } const TransactionCardGroup: FC<{ - transactions: State.Transaction[] + transactions: Transaction[] type: TxStatus sortButton: (sortRule?: ConfirmedSortByType | PendingSortByType) => ReactNode }> = ({ transactions, type, sortButton }) => { const { t } = useTranslation() - const itemHash: SortItemCardData = { + const itemHash: SortItemCardData = { title: t('transaction.transaction_hash'), render: transaction => ( ), } - const itemCapacity: SortItemCardData = { + const itemCapacity: SortItemCardData = { title: t('transaction.capacity'), sortRule: 'capacity', render: transaction => ( @@ -59,7 +60,7 @@ const TransactionCardGroup: FC<{ ), } - const confirmedItems: SortItemCardData[] = [ + const confirmedItems: SortItemCardData[] = [ itemHash, { title: t('transaction.height'), @@ -77,7 +78,7 @@ const TransactionCardGroup: FC<{ }, ] - const pendingItems: SortItemCardData[] = [ + const pendingItems: SortItemCardData[] = [ itemHash, itemCapacity, { @@ -102,7 +103,7 @@ const TransactionCardGroup: FC<{
{items .filter(data => data.sortRule) - .map((data: SortItemCardData) => ( + .map((data: SortItemCardData) => (
{data.title}
{sortButton(data.sortRule)} @@ -120,13 +121,13 @@ const TransactionCardGroup: FC<{ } const TransactionTable: FC<{ - transactions: State.Transaction[] + transactions: Transaction[] type: TxStatus sortButton: (sortRule: ConfirmedSortByType | PendingSortByType) => ReactNode }> = ({ transactions, type, sortButton }) => { const [t] = useTranslation() - const colHash: Column = { + const colHash: Column = { key: 'hash', title: t('transaction.transaction_hash'), className: styles.colHash, @@ -135,7 +136,7 @@ const TransactionTable: FC<{ render: transaction => {transaction.transactionHash}, } - const confirmedColumns: Column[] = [ + const confirmedColumns: Column[] = [ colHash, { key: 'height', @@ -170,7 +171,7 @@ const TransactionTable: FC<{ }, ] - const pendingColumns: Column[] = [ + const pendingColumns: Column[] = [ colHash, { key: 'capacity', diff --git a/src/routes/state.ts b/src/routes/state.ts index 2fcd6b12c..1459e0daa 100644 --- a/src/routes/state.ts +++ b/src/routes/state.ts @@ -1,8 +1,11 @@ +import { Block } from '../models/Block' +import { Transaction } from '../models/Transaction' + export interface RouteState$BlockListPage { type: 'BlockListPage' createTime: number blocksDataWithFirstPage: { - blocks: State.Block[] + blocks: Block[] total: number } } @@ -11,7 +14,7 @@ export interface RouteState$TransactionListPage { type: 'TransactionListPage' createTime: number transactionsDataWithFirstPage: { - transactions: State.Transaction[] + transactions: Transaction[] total: number } } diff --git a/src/services/ExplorerService/fetcher.ts b/src/services/ExplorerService/fetcher.ts index 8687340a7..2a278e843 100644 --- a/src/services/ExplorerService/fetcher.ts +++ b/src/services/ExplorerService/fetcher.ts @@ -5,8 +5,14 @@ import { ReactNode } from 'react' import { pick } from '../../utils/object' import { toCamelcase } from '../../utils/util' import { requesterV1, requesterV2 } from './requester' -import { Response } from './types' +import { ChartItem, NervosDaoDepositor, Response, SupportedExportTransactionType, TransactionRecord } from './types' import { assert } from '../../utils/error' +import { Cell } from '../../models/Cell' +import { Script } from '../../models/Script' +import { Block } from '../../models/Block' +import { Transaction } from '../../models/Transaction' +import { Address } from '../../models/Address' +import { UDT } from '../../models/UDT' async function v1Get(...args: Parameters) { return requesterV1.get(...args).then(res => toCamelcase>(res.data)) @@ -45,7 +51,7 @@ export enum SearchResultType { export const apiFetcher = { fetchBlocks: (page: number, size: number, sort?: string) => - v1Get[]>('blocks', { + v1Get[]>('blocks', { params: { page, page_size: size, @@ -56,15 +62,15 @@ export const apiFetcher = { fetchLatestBlocks: (size: number) => apiFetcher.fetchBlocks(1, size), fetchAddressInfo: (address: string) => - v1GetWrapped(`addresses/${address}`).then( - (wrapper): State.Address => ({ + v1GetWrapped
(`addresses/${address}`).then( + (wrapper): Address => ({ ...wrapper.attributes, type: wrapper.type === 'lock_hash' ? 'LockHash' : 'Address', }), ), fetchTransactionsByAddress: (address: string, page: number, size: number, sort?: string, txTypeFilter?: string) => - v1GetUnwrappedPagedList(`address_transactions/${address}`, { + v1GetUnwrappedPagedList(`address_transactions/${address}`, { params: { page, page_size: size, @@ -75,15 +81,15 @@ export const apiFetcher = { fetchTransactionRaw: (hash: string) => requesterV2.get(`transactions/${hash}/raw`).then(res => res.data), - fetchTransactionByHash: (hash: string) => v1GetUnwrapped(`transactions/${hash}`), + fetchTransactionByHash: (hash: string) => v1GetUnwrapped(`transactions/${hash}`), fetchTransactionLiteDetailsByHash: (hash: string) => requesterV2 .get(`ckb_transactions/${hash}/details`) - .then((res: AxiosResponse) => toCamelcase>(res.data)), + .then((res: AxiosResponse) => toCamelcase>(res.data)), fetchTransactions: (page: number, size: number, sort?: string) => - v1GetUnwrappedPagedList('transactions', { + v1GetUnwrappedPagedList('transactions', { params: { page, page_size: size, @@ -102,7 +108,7 @@ export const apiFetcher = { sort, }, }) - .then(res => toCamelcase>(res.data)) + .then(res => toCamelcase>(res.data)) .then(res => { assert(res.meta, 'Unexpected paged list response') return { @@ -125,7 +131,7 @@ export const apiFetcher = { filter: string | null }>, ) => - v1GetUnwrappedPagedList(`/block_transactions/${blockHash}`, { + v1GetUnwrappedPagedList(`/block_transactions/${blockHash}`, { params: { page, page_size, @@ -134,10 +140,10 @@ export const apiFetcher = { }, }), - fetchBlock: (blockHeightOrHash: string) => v1GetUnwrapped(`blocks/${blockHeightOrHash}`), + fetchBlock: (blockHeightOrHash: string) => v1GetUnwrapped(`blocks/${blockHeightOrHash}`), fetchScript: (scriptType: 'lock_scripts' | 'type_scripts', id: string) => - v1GetNullableWrapped(`/cell_output_${scriptType}/${id}`), + v1GetNullableWrapped