diff --git a/src/components/ProjectHeader/ProjectHeader.tsx b/src/components/ProjectHeader/ProjectHeader.tsx index 3d698396e..343278c8a 100644 --- a/src/components/ProjectHeader/ProjectHeader.tsx +++ b/src/components/ProjectHeader/ProjectHeader.tsx @@ -37,8 +37,8 @@ const ProjectHeader: React.FC = ({ }) => { const { t } = useTranslation(); - const createdAtStr = React.useMemo(() => dayjs(project.createdTimestamp).fromNow(), [project]); - const updatedAtStr = React.useMemo(() => dayjs(project.updatedTimestamp).fromNow(), [project]); + const createdAtStr = React.useMemo(() => dayjs(project.createdTimestamp).utc(true).fromNow(), [project]); + const updatedAtStr = React.useMemo(() => dayjs(project.updatedTimestamp).utc(true).fromNow(), [project]); const VersionDropdown = () => { if (!versions) return <>; diff --git a/src/config/rainbowConf.tsx b/src/config/rainbowConf.tsx index dc8c1ed51..0ec5d5833 100644 --- a/src/config/rainbowConf.tsx +++ b/src/config/rainbowConf.tsx @@ -20,7 +20,8 @@ import '@rainbow-me/rainbowkit/styles.css'; const supportedChains = import.meta.env.VITE_NETWORK === 'testnet' ? [baseSepolia, sepolia] : [base, mainnet]; export const tipsChainIds: number[] = import.meta.env.VITE_NETWORK === 'testnet' ? [baseSepolia.id] : [base.id]; -export const tipsL1ChainIds: number[] = import.meta.env.VITE_NETWORK === 'testnet' ? [sepolia.id] : [mainnet.id]; +export const tipsL1ChainIds: number[] = + import.meta.env.VITE_NETWORK === 'testnet' ? [sepolia.id, baseSepolia.id] : [mainnet.id, base.id]; // This should ok. It seems is a bug of Ts. // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore diff --git a/src/containers/Web3.tsx b/src/containers/Web3.tsx index 6d9b8cada..66a437758 100644 --- a/src/containers/Web3.tsx +++ b/src/containers/Web3.tsx @@ -1,11 +1,11 @@ // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors // SPDX-License-Identifier: Apache-2.0 -import React from 'react'; import mainnetJSON from '@subql/contract-sdk/publish/mainnet.json'; import testnetJSON from '@subql/contract-sdk/publish/testnet.json'; import { NETWORKS_CONFIG_INFO, SQNetworks } from '@subql/network-config'; -import { useAccount } from 'wagmi'; +import { base, baseSepolia } from 'viem/chains'; +import { mainnet, sepolia, useAccount } from 'wagmi'; export const NETWORK_NAME: SQNetworks = import.meta.env.VITE_NETWORK; export const isMainnet = import.meta.env.VITE_NETWORK === 'mainnet'; @@ -16,6 +16,9 @@ export const ECOSYSTEM_NETWORK = NETWORKS_CONFIG_INFO[SUPPORTED_NETWORK].chainNa export const NETWORK_DEPLOYMENT_DETAILS = isMainnet ? mainnetJSON : testnetJSON; +export const l1Chain = import.meta.env.VITE_NETWORK === 'testnet' ? sepolia : mainnet; +export const l2Chain = import.meta.env.VITE_NETWORK === 'testnet' ? baseSepolia : base; + // TODO: FIXME, Mainnet dont have this yet // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore diff --git a/src/hooks/useInitContracts.ts b/src/hooks/useInitContracts.ts index 35d43346c..94afcc884 100644 --- a/src/hooks/useInitContracts.ts +++ b/src/hooks/useInitContracts.ts @@ -2,14 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import React from 'react'; -import { NETWORK_NAME } from '@containers/Web3'; -import mainnetJSON from '@subql/contract-sdk/publish/mainnet.json'; -import testnetJSON from '@subql/contract-sdk/publish/testnet.json'; +import { l1Chain, l2Chain, NETWORK_NAME } from '@containers/Web3'; +import { RootContractSDK } from '@subql/contract-sdk/rootSdk'; import { ContractSDK } from '@subql/contract-sdk/sdk'; -import { SQToken__factory } from '@subql/contract-sdk/typechain/factories/contracts/root/SQToken__factory'; import { ContractClient } from '@subql/network-clients'; import { parseError } from '@utils'; -import { mainnet, sepolia } from 'viem/chains'; +import { base, baseSepolia, mainnet, sepolia } from 'viem/chains'; import { useWeb3Store } from 'src/stores'; @@ -23,12 +21,18 @@ export function useInitContracts(): { loading: boolean } { const ethereumProvider = useEthersProviderWithPublic({ chainId: import.meta.env.MODE === 'testnet' ? sepolia.id : mainnet.id, }); + const baseProvider = useEthersProviderWithPublic({ + chainId: import.meta.env.MODE === 'testnet' ? baseSepolia.id : base.id, + }); React.useEffect(() => { function initContract() { if (signer || provider) { try { - const contractInstance = ContractSDK.create(signer || provider, { network: NETWORK_NAME }); + const contractInstance = ContractSDK.create( + signer?.provider.network.chainId === l2Chain.id ? signer : baseProvider, + { network: NETWORK_NAME }, + ); setContracts(contractInstance); @@ -42,13 +46,10 @@ export function useInitContracts(): { loading: boolean } { } if (ethereumProvider) { - const sqTokenContract = SQToken__factory.connect( - import.meta.env.MODE === 'testnet' ? testnetJSON.root.SQToken.address : mainnetJSON.root.SQToken.address, - ethereumProvider, + const rootContractInstance = RootContractSDK.create( + signer?.provider.network.chainId === l1Chain.id ? signer : ethereumProvider, + { network: NETWORK_NAME }, ); - const rootContractInstance = { - sqToken: sqTokenContract, - }; setRootContracts(rootContractInstance); } } diff --git a/src/pages/bridge/index.module.less b/src/pages/bridge/index.module.less index 384f092ce..c11a0e726 100644 --- a/src/pages/bridge/index.module.less +++ b/src/pages/bridge/index.module.less @@ -11,8 +11,9 @@ flex-direction: column; align-items: center; height: 100%; - padding: 40px; - max-width: 568px; + padding: 40px 0; + max-width: 582px; + width: 582px; gap: 10px; &Tabs { @@ -34,13 +35,13 @@ } .ant-tabs-ink-bar { - background: linear-gradient(96.26deg, #4388DD 13.8%, #FF4581 82.83%); + background: linear-gradient(96.26deg, #4388dd 13.8%, #ff4581 82.83%); } } } &Content { - border: 1px solid #DFE3E899; + border: 1px solid #dfe3e899; border-radius: 8px; padding: 24px; width: 100%; @@ -52,8 +53,8 @@ .smallCard { border-radius: 8px; border: 1px solid var(--sq-gray300); - width: 100%; - + width: 360px; + .top { background: var(--sq-gray200); padding: 16px; @@ -78,6 +79,9 @@ .input { text-align: center; border: none; + width: 100%; + box-shadow: none; + &:focus { border: none; outline: none; @@ -87,6 +91,69 @@ &:focus-visible { outline: none; } + + :global { + .ant-input-number-input { + text-align: center; + border: none; + &-wrap { + &:focus { + border: none; + outline: none; + box-shadow: none; + } + + &:focus-visible { + outline: none; + } + } + } + } + } + } +} + +.pendingAction { + display: flex; + flex-direction: column; + align-items: center; + gap: 24px; + border: 1px solid #DFE3E899; + border-radius: 8px; + padding: 24px; + width: 100%; +} + +.confirmModal { + :global { + .ant-modal-content { + padding: 16px 32px; + } + + .ant-modal-confirm-paragraph { + row-gap: 16px; + } + + .ant-modal-confirm-content { + padding: 24px 0 0 0; + position: relative; + &::before { + content: ' '; + height: 1px; + width: 572px; + background: #F4F6F8; + position: absolute; + top: 0; + left: -32px; + } + } + + .ant-modal-confirm-btns { + margin: 36px 0 16px 0; + } + + .ant-modal-confirm-title { + font-family: var(--sq-font-family); } } } diff --git a/src/pages/bridge/index.tsx b/src/pages/bridge/index.tsx index bec556d28..d8ef58534 100644 --- a/src/pages/bridge/index.tsx +++ b/src/pages/bridge/index.tsx @@ -1,27 +1,33 @@ // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors // SPDX-License-Identifier: Apache-2.0 -import React, { FC, useEffect, useState } from 'react'; +import React, { FC, useEffect, useMemo, useRef, useState } from 'react'; import { BsArrowDownSquareFill, BsLifePreserver } from 'react-icons/bs'; import { useNavigate } from 'react-router'; +import { gql, useQuery } from '@apollo/client'; import { WalletRoute } from '@components'; import { useSQToken } from '@containers'; +import { l1Chain, l2Chain } from '@containers/Web3'; import { CrossChainMessenger } from '@eth-optimism/sdk/src/cross-chain-messenger'; +import { MessageStatus } from '@eth-optimism/sdk/src/interfaces/types'; import { publicClientToProvider, useEthersSigner } from '@hooks/useEthersProvider'; -import { Footer, openNotification, Spinner, Typography } from '@subql/components'; +import { Footer, Modal, openNotification, Spinner, Steps, Typography } from '@subql/components'; import mainnetJSON from '@subql/contract-sdk/publish/mainnet.json'; import testnetJSON from '@subql/contract-sdk/publish/testnet.json'; -import { formatEther } from '@subql/react-hooks'; -import { Button, Input, Tabs } from 'antd'; +import { formatEther, formatSQT } from '@subql/react-hooks'; +import { parseError } from '@utils'; +import { makeCacheKey } from '@utils/limitation'; +import { useInterval } from 'ahooks'; +import { Button, InputNumber, Tabs } from 'antd'; +import { clsx } from 'clsx'; +import dayjs from 'dayjs'; +import { ethers } from 'ethers'; import { parseEther } from 'ethers/lib/utils'; -import { base, baseSepolia } from 'viem/chains'; -import { mainnet, sepolia, usePublicClient } from 'wagmi'; +import localforage from 'localforage'; +import { useAccount, useNetwork, usePublicClient, useSwitchNetwork } from 'wagmi'; import styles from './index.module.less'; -const l1Chain = import.meta.env.VITE_NETWORK === 'testnet' ? sepolia : mainnet; -const l2Chain = import.meta.env.VITE_NETWORK === 'testnet' ? baseSepolia : base; - const l1ContractTokenAddress = import.meta.env.VITE_NETWORK === 'testnet' ? testnetJSON.root.SQToken.address : mainnetJSON.root.SQToken.address; const l2ContractTokenAddress = @@ -29,23 +35,121 @@ const l2ContractTokenAddress = ? testnetJSON.child.L2SQToken.address : mainnetJSON.child.L2SQToken.address; -const Bridge: FC = () => { - const { ethSqtBalance } = useSQToken(); +export interface WithdrawsRecord { + withdraws: { + nodes: { + sender: string; + txHash: string; + nodeId: string; + id: string; + createAt: Date; + blockheight: number; + amount: string; + }[]; + }; +} + +const btnMsgs = { + [MessageStatus.READY_TO_PROVE]: 'Approve withdraw', + [MessageStatus.READY_FOR_RELAY]: 'Withdraw SQT', + [MessageStatus.STATE_ROOT_NOT_PUBLISHED]: 'State root not published', + [MessageStatus.RELAYED]: 'Finished, please wait at most 7 days and check in your wallet', + + // fallback + [MessageStatus.FAILED_L1_TO_L2_MESSAGE]: 'Failed l1 to l2 message', + [MessageStatus.IN_CHALLENGE_PERIOD]: 'In challenge period', + [MessageStatus.UNCONFIRMED_L1_TO_L2_MESSAGE]: 'Unconfirmed l1 to l2 message', +}; + +const descMsgs = { + [MessageStatus.READY_TO_PROVE]: 'Ready to prove, please do it as soon as possiable', + [MessageStatus.READY_FOR_RELAY]: 'Ready to withdraw', + [MessageStatus.STATE_ROOT_NOT_PUBLISHED]: 'State root not published, please wait...', + [MessageStatus.RELAYED]: 'You must wait for a 7 days withdraw period required by OP stack.', + + // fallback + [MessageStatus.FAILED_L1_TO_L2_MESSAGE]: 'Failed l1 to l2 message', + [MessageStatus.IN_CHALLENGE_PERIOD]: 'In challenge period', + [MessageStatus.UNCONFIRMED_L1_TO_L2_MESSAGE]: 'Unconfirmed l1 to l2 message', +}; + +const BridgeInner: FC = () => { + const { ethSqtBalance, balance } = useSQToken(); const { signer } = useEthersSigner(); + const { chain } = useNetwork(); + const { address: account } = useAccount(); + + const publicClientL1 = usePublicClient({ chainId: l1Chain.id }); + const { switchNetwork } = useSwitchNetwork(); const navigate = useNavigate(); + const withdrawsRecord = useQuery( + gql` + query getWithdrawsRecord($sender: String!) { + withdraws(filter: { sender: { equalTo: $sender } }, orderBy: CREATE_AT_DESC) { + nodes { + sender + txHash + nodeId + id + createAt + blockheight + amount + } + } + } + `, + { + variables: { + sender: account, + }, + fetchPolicy: 'network-only', + }, + ); const [val, setVal] = useState('0'); const [loading, setLoading] = useState(false); + const [startL2BridgeLoading, setStartL2BridgeLoading] = useState(false); + const [withdrawLoading, setWithdrawLoading] = useState(false); const [crossChainMessengerIns, setCrossChainMessengerIns] = useState(); - const publicClient = usePublicClient({ chainId: l2Chain.id }); + const [pendingActionStatus, setPendingActionStatus] = useState<{ + [key: string]: { status: MessageStatus; startTime?: number }; + }>({}); + const [currentTab, setCurrentTab] = useState<'ethToBase' | 'baseToEth'>('ethToBase'); + // This state is used to prevent the fetchPendingActionStatus function from being called when finalizeMessage is called. + const shouldFetch = useRef(true); + + const cacheKey = useMemo(() => makeCacheKey(account || '', { prefix: 'pendingActionStatus' }), [account]); + + const sortedWithdrawRecords = useMemo(() => { + return ( + withdrawsRecord.data?.withdraws.nodes.filter(({ txHash }) => { + const status = pendingActionStatus[txHash]?.status; + if (status === MessageStatus.RELAYED) { + if (pendingActionStatus[txHash].startTime) { + const durationTime = +dayjs() - (pendingActionStatus[txHash].startTime as number); + const duration = dayjs.duration(durationTime); + const maxPeiod = dayjs.duration(7, 'day'); + const leftTime = maxPeiod.subtract(duration); + + if (leftTime.days() === 0 && leftTime.hours() === 0) { + return false; + } + } + } + + return true; + }) || [] + ); + }, [pendingActionStatus, withdrawsRecord.data?.withdraws.nodes]); const initCrossChainMessenger = async () => { if (!signer) return; - + const l2Provider = new ethers.providers.JsonRpcProvider(l2Chain.rpcUrls.public.http[0]); const newCrossChainMessenger = new CrossChainMessenger({ l1ChainId: l1Chain.id, l2ChainId: l2Chain.id, - l1SignerOrProvider: signer, - l2SignerOrProvider: publicClientToProvider(publicClient), + l1SignerOrProvider: + signer.provider.network.chainId === l1Chain.id ? signer : publicClientToProvider(publicClientL1), + l2SignerOrProvider: signer.provider.network.chainId === l2Chain.id ? signer : l2Provider, }); setCrossChainMessengerIns(newCrossChainMessenger); @@ -84,80 +188,393 @@ const Bridge: FC = () => { await ethSqtBalance.refetch(); navigate('/bridge/success'); } catch (e: any) { - console.error(e); openNotification({ type: 'error', - description: e.message, + description: parseError(e.message), }); } finally { setLoading(false); } }; + const withdrawStart = async () => { + if (!crossChainMessengerIns) return; + + const _withdrawStart = async () => { + try { + setStartL2BridgeLoading(true); + const amount = parseEther(val); + const withdrawTx = await crossChainMessengerIns.withdrawERC20( + l1ContractTokenAddress, + l2ContractTokenAddress, + amount, + ); + await withdrawTx.wait(); + openNotification({ + type: 'success', + description: + 'Withdraw request success, please wait L1 status changed, you can check the status at below list.', + duration: 5, + }); + } catch (e: any) { + openNotification({ + type: 'error', + description: parseError(e.message), + }); + } finally { + setStartL2BridgeLoading(false); + } + }; + + Modal.confirm({ + width: 572, + className: styles.confirmModal, + title: 'Confirm Bridge', + closable: true, + icon: null, + content: ( +
+
+
+
+ + {val || '0'} + +
+
+ + SQT +
+
+ +
+
+ + {val || '0'} + +
+
+ + SQT +
+
+
+ + OP stack and Base requires that any bridging to Ethereum requires a 7 day withdraw period. If you do not + want to wait for the required 7 days, please consider a{' '} + + third party bridge + + . + +
+ ), + cancelButtonProps: { + style: { display: 'none' }, + }, + okText: `Confirm Bridge for ${(+val).toLocaleString()} SQT`, + okButtonProps: { + shape: 'round', + }, + onOk: async () => { + await _withdrawStart(); + }, + }); + }; + + const fetchPendingActionStatus = async () => { + if (!crossChainMessengerIns || !account) return; + + const cachedStorage = await localforage.getItem<{ [key: string]: { status: MessageStatus; startTime?: number } }>( + cacheKey, + ); + const filterStatus = (tx: string) => { + if (!cachedStorage) return true; + try { + if (cachedStorage[tx].status === MessageStatus.RELAYED) return false; + return true; + } catch (e) { + return true; + } + }; + const sortedWithdrawsRecord = withdrawsRecord.data?.withdraws.nodes.filter((i) => filterStatus(i.txHash)); + + const innerPendingActionStatus: { [key: string]: { status: MessageStatus; startTime?: number } } = {}; + for (const item of sortedWithdrawsRecord || []) { + const status = await crossChainMessengerIns.getMessageStatus(item.txHash); + innerPendingActionStatus[item.txHash] = { status }; + if (pendingActionStatus[item.txHash]?.startTime) { + innerPendingActionStatus[item.txHash]['startTime'] = pendingActionStatus[item.txHash].startTime; + } + } + + const mergeFetchStatus = () => { + if (!cachedStorage) return innerPendingActionStatus; + + try { + return { + ...cachedStorage, + ...innerPendingActionStatus, + }; + } catch (e) { + console.warn(e); + return innerPendingActionStatus; + } + }; + if (shouldFetch.current) { + setPendingActionStatus(mergeFetchStatus()); + await localforage.setItem(cacheKey, mergeFetchStatus()); + // for next interval + } else { + shouldFetch.current = true; + } + }; + + const withdrawApproveOrFinalize = async (txHash: string) => { + if (!crossChainMessengerIns) return; + try { + setWithdrawLoading(true); + if (pendingActionStatus[txHash]?.status === MessageStatus.READY_TO_PROVE) { + try { + const approveTx = await crossChainMessengerIns.proveMessage(txHash); + await approveTx.wait(); + openNotification({ + type: 'success', + description: 'Approve success, please wait...', + duration: 3, + }); + } catch (e: any) { + openNotification({ + type: 'error', + description: parseError(e.message), + }); + } + } + + if (pendingActionStatus[txHash]?.status === MessageStatus.READY_FOR_RELAY) { + try { + const finalizeTx = await crossChainMessengerIns.finalizeMessage(txHash); + await finalizeTx.wait(); + openNotification({ + type: 'success', + description: 'Finalize success, You must wait for a 7 day withdraw period required by OP stack.', + duration: 3, + }); + await localforage.setItem(cacheKey, { + ...pendingActionStatus, + [txHash]: { + status: MessageStatus.RELAYED, + startTime: +dayjs(), + }, + }); + setPendingActionStatus({ + ...pendingActionStatus, + [txHash]: { + status: MessageStatus.RELAYED, + startTime: +dayjs(), + }, + }); + shouldFetch.current = false; + } catch (e: any) { + openNotification({ + type: 'error', + description: parseError(e.message), + }); + } + } + } finally { + setWithdrawLoading(false); + } + }; + useEffect(() => { initCrossChainMessenger(); }, [signer]); + useEffect(() => { + fetchPendingActionStatus(); + }, [withdrawsRecord.data?.withdraws.nodes, crossChainMessengerIns]); + + useEffect(() => { + fetchPendingActionStatus(); + }, [chain?.id]); + + useInterval(async () => { + await withdrawsRecord.refetch(); + fetchPendingActionStatus(); + }, 10000); + return ( - -
- - Bridge - +
+
+ + Bridge + - - SubQuery mainnet has launched, you can now bridge your SQT from Ethereum to Base to access the SubQuery - Network. - + + SubQuery mainnet has launched, you can now bridge your SQT from Ethereum to Base to access the SubQuery + Network. + + + { + setCurrentTab(key as 'ethToBase' | 'baseToEth'); + setVal('0'); + }} + items={[ + { + label: 'Ethereum -> Base', + key: 'ethToBase', + }, + { + label: 'Base -> Ethereum', + key: 'baseToEth', + }, + ]} + > + + {currentTab === 'ethToBase' && ( +
+
+ + FREE SQT + + + {ethSqtBalance.result.loading ? ( + + ) : ( + formatEther(ethSqtBalance.result.data || 0, 4) + )}{' '} + SQT + +
+
+
+ + SQT +
+ +
+ { + setVal(newVal?.toString() || ''); + }} + > +
+
+ +
+
+ + SQT +
+ +
+ + {val || '0'} + +
+
- BASE', - key: 'ethToBase', - }, - ]} - > +
+ + Transfer time: A few minutes + +
+ + + + + +
+ Need help? please message #network-bridge-support on + Discord +
+
+
+ )} + {currentTab === 'baseToEth' && ( + <>
+ + OP stack and Base requires that any bridging to Ethereum requires a 7 day withdraw period. If you do + not want to wait for the required 7 days, please consider a{' '} + + third party bridge + + . + + + + FREE SQT - {ethSqtBalance.result.loading ? ( - - ) : ( - formatEther(ethSqtBalance.result.data || 0, 4) - )}{' '} + {balance.result.loading ? : formatEther(balance.result.data || 0, 4)}{' '} SQT
- + SQT
- { - const numericValue = newVal.target.value.replace(/[^0-9.]/g, ''); - setVal(numericValue); + setVal(newVal?.toString() || ''); }} - > + >
- +
- + SQT
@@ -168,25 +585,22 @@ const Bridge: FC = () => {
-
- - Transfer time: A few minutes - - {/* - Network fee (est.): 0.000000066 ETH ($0.000194 USD) - */} -
- @@ -197,11 +611,88 @@ const Bridge: FC = () => {
-
-
- - } - >
+ + {sortedWithdrawRecords.length ? ( +
+ + Pending Bridge Actions + + + {sortedWithdrawRecords.map((item) => { + const status = pendingActionStatus[item.txHash]?.status; + + const btnMsgFunc = () => { + if (status === MessageStatus.RELAYED) { + if (pendingActionStatus[item.txHash].startTime) { + const durationTime = +dayjs() - (pendingActionStatus[item.txHash].startTime as number); + const duration = dayjs.duration(durationTime); + const maxPeiod = dayjs.duration(7, 'day'); + const leftTime = maxPeiod.subtract(duration); + + if (leftTime.days() === 0 && leftTime.hours() === 0) { + return `Finished`; + } + + return `Please wait another ${leftTime.days()} days ${leftTime.hours()} hours (estimate)`; + } + } + + return ( + btnMsgs[status] || + `${Object.keys(pendingActionStatus).length === 0 ? 'Fetching status...' : 'Unknown'}` + ); + }; + + const desc = descMsgs[status] || 'Unknown'; + const isL1Chain = chain?.id === l1Chain.id; + const disable = + isL1Chain && status !== MessageStatus.READY_TO_PROVE && status !== MessageStatus.READY_FOR_RELAY; + + return ( +
+ + Bridge for {Number(formatSQT(item.amount)).toLocaleString()} SQT from Base{' '} + {'->'} Ethereum{' '} + + + {desc} + + + { + + } +
+ ); + })} +
+ ) : ( + '' + )} + + )} + +
+ ); }; + +const Bridge: FC = () => { + return }>; +}; export default Bridge; diff --git a/src/pages/indexer/MyDelegators/OwnDelegator.tsx b/src/pages/indexer/MyDelegators/OwnDelegator.tsx index 173ee6110..d575517a3 100644 --- a/src/pages/indexer/MyDelegators/OwnDelegator.tsx +++ b/src/pages/indexer/MyDelegators/OwnDelegator.tsx @@ -101,7 +101,7 @@ export const OwnDelegator: React.FC = ({ indexer, showEmpty, hideCard, sh
- Capcity + Capacity {formatNumber(sortedIndexer.data?.capacity.current || '0')} {TOKEN} diff --git a/src/stores/web3Account.ts b/src/stores/web3Account.ts index 7c55c533a..f66cc11a6 100644 --- a/src/stores/web3Account.ts +++ b/src/stores/web3Account.ts @@ -1,8 +1,8 @@ // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { RootContractSDK } from '@subql/contract-sdk/rootSdk'; import { ContractSDK } from '@subql/contract-sdk/sdk'; -import { SQToken } from '@subql/contract-sdk/typechain/contracts/root/SQToken'; import { ContractClient } from '@subql/network-clients'; import { ethers } from 'ethers'; import { create } from 'zustand'; @@ -13,10 +13,6 @@ import { create } from 'zustand'; * */ -interface rootContracts { - sqToken: SQToken; -} - interface Web3Store { error?: any; setError: (error: any) => void; @@ -32,8 +28,8 @@ interface Web3Store { contracts?: ContractSDK; setContracts: (contracts: ContractSDK) => void; - rootContracts?: rootContracts; - setRootContracts: (rootContracts: rootContracts) => void; + rootContracts?: RootContractSDK; + setRootContracts: (rootContracts: RootContractSDK) => void; contractClient?: ContractClient; setContractClient: (contracts: ContractClient) => void; @@ -44,7 +40,7 @@ export const useWeb3Store = create()((set) => ({ contracts: undefined, isInitialAccount: false, rootContracts: undefined, - setRootContracts: (rootContracts: rootContracts | undefined) => set((state) => ({ ...state, rootContracts })), + setRootContracts: (rootContracts: RootContractSDK | undefined) => set((state) => ({ ...state, rootContracts })), ethProvider: () => { const providers = [ new ethers.providers.JsonRpcProvider('https://eth.llamarpc.com'), diff --git a/src/utils/constants.ts b/src/utils/constants.ts index aa508018c..65f50152d 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -85,8 +85,8 @@ export const rpcCategoriesOptions = [ value: 'EVM', }, { - label: 'Polkdot', - value: 'Polkdot', + label: 'Polkadot', + value: 'Polkadot', }, { label: 'Cosmos', diff --git a/src/utils/parseError.ts b/src/utils/parseError.ts index 145ffb9b2..f3b9a5b55 100644 --- a/src/utils/parseError.ts +++ b/src/utils/parseError.ts @@ -98,7 +98,15 @@ export function parseError( const revertCode = Object.keys(contractErrorCodes).find((key) => rawErrorMsg.toString().match(`reverted: ${key}`), ) as keyof typeof contractErrorCodes; - return revertCode ? contractErrorCodes[revertCode] : undefined; + try { + captureException('Call contract error revert, need review', { + extra: { + error: rawErrorMsg, + }, + }); + } finally { + return revertCode ? contractErrorCodes[revertCode] : undefined; + } }; // https://github.com/ethers-io/ethers.js/discussions/1856 @@ -122,7 +130,11 @@ export function parseError( const generalErrorMsg = () => { try { - captureException(error); + captureException('Unknown error, need review', { + extra: { + error: rawErrorMsg, + }, + }); } finally { return 'Unfortunately, something went wrong.'; }