diff --git a/apps/wallet/src/i18n/locales/cnt.json b/apps/wallet/src/i18n/locales/cnt.json index feb3fca..6273714 100644 --- a/apps/wallet/src/i18n/locales/cnt.json +++ b/apps/wallet/src/i18n/locales/cnt.json @@ -105,6 +105,8 @@ "page_settings_changePassword": "更改錢包密碼", "page_settings_developerMode": "開發者模式", "page_settings_devModeField": "啟用內部測試網", + "page_settings_devModeOnlyMainnet": "仅支持主網鏈", + "page_settings_devModeOnlyTestnet": "仅支持測試網鏈", "page_settings_account": "帳戶", "page_settings_linkMore": "連結更多", "page_settings_language": "語言", diff --git a/apps/wallet/src/i18n/locales/en.json b/apps/wallet/src/i18n/locales/en.json index 59ce84c..b30c09c 100644 --- a/apps/wallet/src/i18n/locales/en.json +++ b/apps/wallet/src/i18n/locales/en.json @@ -110,6 +110,8 @@ "page_settings_changePassword": "Change wallet password", "page_settings_developerMode": "Developer Mode", "page_settings_devModeField": "Enable internal testnet", + "page_settings_devModeOnlyMainnet": "Only mainnet chains are supported", + "page_settings_devModeOnlyTestnet": "Only testnet chains are supported", "page_settings_account": "Account", "page_settings_linkMore": "Link more", "page_settings_language": "Language", diff --git a/apps/wallet/src/i18n/locales/ja.json b/apps/wallet/src/i18n/locales/ja.json index 8e9bafb..6f08fe4 100644 --- a/apps/wallet/src/i18n/locales/ja.json +++ b/apps/wallet/src/i18n/locales/ja.json @@ -105,6 +105,8 @@ "page_settings_changePassword": "ウォレットパスワードの変更", "page_settings_developerMode": "開発者モード", "page_settings_devModeField": "内部テストネットを有効化", + "page_settings_devModeOnlyMainnet": "メインネットのチェーンのみをサポート", + "page_settings_devModeOnlyTestnet": "テストネットのチェーンのみをサポート", "page_settings_account": "アカウント", "page_settings_linkMore": "さらにリンク", "page_settings_language": "言語", diff --git a/apps/wallet/src/i18n/locales/ru.json b/apps/wallet/src/i18n/locales/ru.json index 9b63f33..429aa0a 100644 --- a/apps/wallet/src/i18n/locales/ru.json +++ b/apps/wallet/src/i18n/locales/ru.json @@ -105,6 +105,8 @@ "page_settings_changePassword": "Изменить пароль кошелька", "page_settings_developerMode": "Режим разработчика", "page_settings_devModeField": "Активировать внутреннюю тестовую сеть", + "page_settings_devModeOnlyMainnet": "Поддерживаются только основные сети", + "page_settings_devModeOnlyTestnet": "Поддерживаются только тестовые сети", "page_settings_account": "Аккаунт", "page_settings_linkMore": "Подключить больше", "page_settings_language": "Язык", diff --git a/apps/wallet/src/stores/session.ts b/apps/wallet/src/stores/session.ts index 29527dd..432f3ec 100644 --- a/apps/wallet/src/stores/session.ts +++ b/apps/wallet/src/stores/session.ts @@ -15,6 +15,8 @@ import { HIBIT_ENV } from "../utils/env"; import { getChainByChainId, getDevModeSwitchChain, getSupportedChains } from "../utils/chain"; import { getSystemLang, Language } from "../utils/lang"; import i18n from "../i18n"; +import toaster from "../components/Toaster"; +import { t } from "i18next"; const SESSION_CONFIG_KEY = 'hibit-id-config' const PASSWORD_STORAGE_KEY = 'hibit-id-p' @@ -118,10 +120,17 @@ export class HibitIdSession { public setDevMode = (devMode: boolean) => { if (this.config.devMode === devMode) return this.config.devMode = devMode - localStorage.setItem(SESSION_CONFIG_KEY, JSON.stringify(this.config)) - setTimeout(() => { - const newChain = getDevModeSwitchChain(!devMode, this.chainInfo.chainId) - this.switchChain(newChain) + setTimeout(async () => { + try { + const newChain = getDevModeSwitchChain(!devMode, this.chainInfo.chainId) + await this.switchChain(newChain) + } catch (e) { + console.error(e) + toaster.error(devMode ? t('page_settings_devModeOnlyMainnet') : t('page_settings_devModeOnlyTestnet')) + this.config.devMode = !devMode + } finally { + localStorage.setItem(SESSION_CONFIG_KEY, JSON.stringify(this.config)) + } }) } @@ -184,9 +193,13 @@ export class HibitIdSession { if (!this.walletPool) { throw new HibitIDError(HibitIDErrorCode.WALLET_LOCKED) } - this.setChainInfo(chain) const oldAddress = this._account?.address - this._account = await this.walletPool.getAccount(chain.chainId) + try { + this._account = await this.walletPool.getAccount(chain.chainId) + this.setChainInfo(chain) + } catch (e) { + throw e + } if (RUNTIME_ENV === RuntimeEnv.SDK) { rpcManager.notifyChainChanged(chain) if (oldAddress !== this._account.address) { diff --git a/apps/wallet/src/utils/basicTypes.ts b/apps/wallet/src/utils/basicTypes.ts index bb9f25b..e6416e8 100644 --- a/apps/wallet/src/utils/basicTypes.ts +++ b/apps/wallet/src/utils/basicTypes.ts @@ -278,6 +278,7 @@ export class ChainInfo { supportedSignaturesSchemas!: WalletSignatureSchema[]; explorer!: string; rpcUrls!: string[]; + isMainnet!: boolean caseSensitiveAddress?: boolean; feeTokenType?: 'native' | 'token' = 'native' getServerFormatAddress?: (address: string) => string | null diff --git a/apps/wallet/src/utils/chain/chain-list.ts b/apps/wallet/src/utils/chain/chain-list.ts index 8fbe41f..24ec4f1 100644 --- a/apps/wallet/src/utils/chain/chain-list.ts +++ b/apps/wallet/src/utils/chain/chain-list.ts @@ -11,6 +11,7 @@ export const Ethereum: ChainInfo = { icon: '/chain-icons/Ethereum.svg', nativeAssetSymbol: 'ETH', nativeAssetDecimals: 18, + isMainnet: true, supportedSignaturesSchemas: [WalletSignatureSchema.EvmEcdsa], explorer: 'https://etherscan.io', rpcUrls: ['https://ethereum.blockpi.network/v1/rpc/public'] @@ -22,6 +23,7 @@ export const EthereumSepolia: ChainInfo = { icon: '/chain-icons/Ethereum.svg', nativeAssetSymbol: 'ETH', nativeAssetDecimals: 18, + isMainnet: false, supportedSignaturesSchemas: [WalletSignatureSchema.EvmEcdsa], explorer: 'https://sepolia.etherscan.io', rpcUrls: ['https://ethereum-sepolia.blockpi.network/v1/rpc/public '] @@ -34,6 +36,7 @@ export const EthereumBsc: ChainInfo = { icon: '/chain-icons/BNB.svg', nativeAssetSymbol: 'BNB', nativeAssetDecimals: 18, + isMainnet: true, supportedSignaturesSchemas: [WalletSignatureSchema.EvmEcdsa], explorer: 'https://bscscan.com', rpcUrls: ['https://bsc-dataseed.binance.org'] @@ -45,6 +48,7 @@ export const EthereumBscTestnet: ChainInfo = { icon: '/chain-icons/BNB.svg', nativeAssetSymbol: 'tBNB', nativeAssetDecimals: 18, + isMainnet: false, supportedSignaturesSchemas: [WalletSignatureSchema.EvmEcdsa], explorer: 'https://testnet.bscscan.com', rpcUrls: ['https://bsc-testnet.publicnode.com'] @@ -57,6 +61,7 @@ export const EthereumScroll: ChainInfo = { icon: '/chain-icons/Scroll.svg', nativeAssetSymbol: 'ETH', nativeAssetDecimals: 18, + isMainnet: true, supportedSignaturesSchemas: [WalletSignatureSchema.EvmEcdsa], explorer: 'https://scroll.l2scan.co', rpcUrls: ['https://rpc.scroll.io'] @@ -68,6 +73,7 @@ export const EthereumScrollSepolia: ChainInfo = { icon: '/chain-icons/Scroll.svg', nativeAssetSymbol: 'ETH', nativeAssetDecimals: 18, + isMainnet: false, supportedSignaturesSchemas: [WalletSignatureSchema.EvmEcdsa], explorer: 'https://scroll-sepolia.l2scan.co', rpcUrls: ['https://sepolia-rpc.scroll.io'] @@ -80,6 +86,7 @@ export const EthereumBase: ChainInfo = { icon: '/chain-icons/Base.svg', nativeAssetSymbol: 'ETH', nativeAssetDecimals: 18, + isMainnet: true, supportedSignaturesSchemas: [WalletSignatureSchema.EvmEcdsa], explorer: 'https://basescan.org', rpcUrls: ['https://mainnet.base.org'] @@ -91,6 +98,7 @@ export const EthereumBaseSepolia: ChainInfo = { icon: '/chain-icons/Base.svg', nativeAssetSymbol: 'ETH', nativeAssetDecimals: 18, + isMainnet: false, supportedSignaturesSchemas: [WalletSignatureSchema.EvmEcdsa], explorer: 'https://sepolia.basescan.org', rpcUrls: ['https://sepolia.base.org'] @@ -103,6 +111,7 @@ export const EthereumAvalanche: ChainInfo = { icon: '/chain-icons/Avalanche.svg', nativeAssetSymbol: 'AVAX', nativeAssetDecimals: 18, + isMainnet: true, supportedSignaturesSchemas: [WalletSignatureSchema.EvmEcdsa], explorer: 'https://snowtrace.io', rpcUrls: ['https://api.avax.network/ext/bc/C/rpc'] @@ -114,6 +123,7 @@ export const EthereumAvalancheFuji: ChainInfo = { icon: '/chain-icons/Avalanche.svg', nativeAssetSymbol: 'AVAX', nativeAssetDecimals: 18, + isMainnet: false, supportedSignaturesSchemas: [WalletSignatureSchema.EvmEcdsa], explorer: 'https://subnets-test.avax.network/c-chain', rpcUrls: ['https://api.avax-test.network/ext/bc/C/rpc'] @@ -126,6 +136,7 @@ export const EthereumBitlayer: ChainInfo = { icon: '/chain-icons/Bitlayer.png', nativeAssetSymbol: 'BTC', nativeAssetDecimals: 18, + isMainnet: true, supportedSignaturesSchemas: [WalletSignatureSchema.EvmEcdsa], explorer: 'https://btrscan.com', rpcUrls: ['https://rpc.bitlayer.org'] @@ -137,6 +148,7 @@ export const EthereumBitlayerTestnet: ChainInfo = { icon: '/chain-icons/Bitlayer.png', nativeAssetSymbol: 'BTC', nativeAssetDecimals: 18, + isMainnet: false, supportedSignaturesSchemas: [WalletSignatureSchema.EvmEcdsa], explorer: 'https://testnet.btrscan.com', rpcUrls: ['https://testnet-rpc.bitlayer.org'] @@ -149,6 +161,7 @@ export const Bitcoin: ChainInfo = { icon: '/chain-icons/Bitcoin.svg', nativeAssetSymbol: 'BTC', nativeAssetDecimals: 8, + isMainnet: true, supportedSignaturesSchemas: [WalletSignatureSchema.BtcEcdsa], explorer: 'https://blockstream.info', rpcUrls: [] @@ -160,6 +173,7 @@ export const BitcoinTestnet: ChainInfo = { icon: '/chain-icons/Bitcoin.svg', nativeAssetSymbol: 'BTC', nativeAssetDecimals: 8, + isMainnet: false, supportedSignaturesSchemas: [WalletSignatureSchema.BtcEcdsa], explorer: 'https://blockstream.info/testnet', rpcUrls: [] @@ -172,6 +186,7 @@ export const Solana: ChainInfo = { icon: '/chain-icons/Solana.svg', nativeAssetSymbol: 'SOL', nativeAssetDecimals: 9, + isMainnet: true, supportedSignaturesSchemas: [WalletSignatureSchema.SolanaEddsa], explorer: 'https://explorer.solana.com', rpcUrls: [clusterApiUrl('mainnet-beta')], @@ -184,6 +199,7 @@ export const SolanaTestnet: ChainInfo = { icon: '/chain-icons/Solana.svg', nativeAssetSymbol: 'SOL', nativeAssetDecimals: 9, + isMainnet: false, supportedSignaturesSchemas: [WalletSignatureSchema.SolanaEddsa], explorer: 'https://explorer.solana.com?cluster=testnet', rpcUrls: [clusterApiUrl('testnet')], @@ -197,6 +213,7 @@ export const Ton: ChainInfo = { icon: '/chain-icons/Ton.svg', nativeAssetSymbol: 'TON', nativeAssetDecimals: 9, + isMainnet: true, supportedSignaturesSchemas: [WalletSignatureSchema.TonEddsaOpenMask], explorer: 'https://tonviewer.com', rpcUrls: [], @@ -246,6 +263,7 @@ export const TonTestnet: ChainInfo = { icon: '/chain-icons/Ton.svg', nativeAssetSymbol: 'TON', nativeAssetDecimals: 9, + isMainnet: false, supportedSignaturesSchemas: [WalletSignatureSchema.TonEddsaOpenMask], explorer: 'https://testnet.tonviewer.com', rpcUrls: [], @@ -304,6 +322,7 @@ export const Tron: ChainInfo = { icon: '/chain-icons/Tron.svg', nativeAssetSymbol: 'TRX', nativeAssetDecimals: 6, + isMainnet: true, supportedSignaturesSchemas: [WalletSignatureSchema.TronEcdsa], explorer: 'https://tronscan.org', rpcUrls: ['https://rpc.trongrid.io'], @@ -329,6 +348,7 @@ export const TronShasta: ChainInfo = { icon: '/chain-icons/Tron.svg', nativeAssetSymbol: 'TRX', nativeAssetDecimals: 6, + isMainnet: false, supportedSignaturesSchemas: [WalletSignatureSchema.TronEcdsa], explorer: 'https://shasta.tronscan.org', rpcUrls: ['https://api.shasta.trongrid.io'], @@ -354,6 +374,7 @@ export const TronNile: ChainInfo = { icon: '/chain-icons/Tron.svg', nativeAssetSymbol: 'TRX', nativeAssetDecimals: 6, + isMainnet: false, supportedSignaturesSchemas: [WalletSignatureSchema.TronEcdsa], explorer: 'https://nile.tronscan.org', rpcUrls: ['https://api.nileex.io'], @@ -380,6 +401,7 @@ export const Dfinity: ChainInfo = { icon: '/chain-icons/IC.svg', nativeAssetSymbol: 'ICP', nativeAssetDecimals: 8, + isMainnet: true, supportedSignaturesSchemas: [WalletSignatureSchema.IcpEddsa], explorer: 'https://dashboard.internetcomputer.org', rpcUrls: ['https://ic0.app'], diff --git a/apps/wallet/src/utils/chain/chain-wallets/index.ts b/apps/wallet/src/utils/chain/chain-wallets/index.ts index 33235e3..e3bf874 100644 --- a/apps/wallet/src/utils/chain/chain-wallets/index.ts +++ b/apps/wallet/src/utils/chain/chain-wallets/index.ts @@ -41,25 +41,45 @@ export class ChainWalletPool { } getAccount = async (chainId: ChainId): Promise => { - return await this.get(chainId).getAccount() + try { + return await this.get(chainId).getAccount() + } catch (e) { + throw e + } } signMessage = async (message: string, chainId: ChainId): Promise => { - return await this.get(chainId).signMessage(message) + try { + return await this.get(chainId).signMessage(message) + } catch (e) { + throw e + } } balanceOf = async (address: string, assetInfo: AssetInfo): Promise => { - const chainId = new ChainId(assetInfo.chain, assetInfo.chainNetwork) - return await this.get(chainId).balanceOf(address, assetInfo) + try { + const chainId = new ChainId(assetInfo.chain, assetInfo.chainNetwork) + return await this.get(chainId).balanceOf(address, assetInfo) + } catch (e) { + throw e + } } transfer = async (toAddress: string, amount: BigNumber, assetInfo: AssetInfo): Promise => { - const chainId = new ChainId(assetInfo.chain, assetInfo.chainNetwork) - return await this.get(chainId).transfer(toAddress, amount, assetInfo) + try { + const chainId = new ChainId(assetInfo.chain, assetInfo.chainNetwork) + return await this.get(chainId).transfer(toAddress, amount, assetInfo) + } catch (e) { + throw e + } } getEstimatedFee = async (toAddress: string, amount: BigNumber, assetInfo: AssetInfo): Promise => { - const chainId = new ChainId(assetInfo.chain, assetInfo.chainNetwork) - return await this.get(chainId).getEstimatedFee(toAddress, amount, assetInfo) + try { + const chainId = new ChainId(assetInfo.chain, assetInfo.chainNetwork) + return await this.get(chainId).getEstimatedFee(toAddress, amount, assetInfo) + } catch (e) { + throw e + } } } diff --git a/apps/wallet/src/utils/chain/index.ts b/apps/wallet/src/utils/chain/index.ts index f64ec28..33cce76 100644 --- a/apps/wallet/src/utils/chain/index.ts +++ b/apps/wallet/src/utils/chain/index.ts @@ -3,6 +3,8 @@ import { Ethereum, EthereumAvalanche, EthereumAvalancheFuji, EthereumBase, Ether import { ChainInfo } from '../basicTypes'; import { RUNTIME_SUPPORTED_CHAIN_IDS } from '../runtime'; import hibitIdSession from '../../stores/session'; +import { HIBIT_ENV } from '../env'; +import { HibitEnv } from '../basicEnums'; // TODO: should update when we support more chains const SupportedChainsForMainnet = [ @@ -29,8 +31,10 @@ const SupportedChainsForTestnet = [ // SolanaTestnet, TonTestnet, // TronNile, - Dfinity, ]; +if (HIBIT_ENV !== HibitEnv.PROD) { + SupportedChainsForTestnet.push(Dfinity) +} export function getChainByChainId(chainId: ChainId | null, devMode?: boolean): ChainInfo | null { if (!chainId) return null; @@ -51,9 +55,9 @@ export function getSupportedChains(devMode?: boolean, chainTypes?: Chain[]): Cha } export function getDevModeSwitchChain(isCurrentDevMode: boolean, chainId: ChainId): ChainInfo { - const [currentList, mappingList] = isCurrentDevMode - ? [SupportedChainsForTestnet, SupportedChainsForMainnet] - : [SupportedChainsForMainnet, SupportedChainsForTestnet]; - const index = currentList.findIndex(c => c.chainId.equals(chainId)); - return index < 0 ? mappingList[0] : mappingList[index]; + const devModeChains = getSupportedChains(true) + const nonDevModeChains = getSupportedChains(false) + const mappingList = isCurrentDevMode ? nonDevModeChains : devModeChains; + const newChain = mappingList.find(c => c.chainId.type.equals(chainId.type) && c.isMainnet === !isCurrentDevMode); + return !newChain ? mappingList[0] : newChain; }