diff --git a/bun.lockb b/bun.lockb index 8c3b33a..dbfca94 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 5dbd3d7..4011b81 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@skalenetwork/metaport", - "version": "2.3.0", + "version": "3.0.0", "description": "SKALE Metaport Widget", "keywords": [ "skale", @@ -75,17 +75,18 @@ "vitest": "0.34.1" }, "dependencies": { - "@mui/material": "^5.15.6", - "@mui/lab": "^5.0.0-alpha.162", - "@mui/icons-material": "^5.15.6", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", - "@rainbow-me/rainbowkit": "^1.3.6", + "@mui/icons-material": "^5.15.6", + "@mui/lab": "^5.0.0-alpha.162", + "@mui/material": "^5.15.6", + "@rainbow-me/rainbowkit": "^2.1.4", "@skalenetwork/ima-js": "2.0.2-develop.0", + "@tanstack/react-query": "^5.51.23", "coingecko-api-v3": "^0.0.29", "react-jazzicon": "^1.0.4", - "viem": "^1.21.4", - "wagmi": "^1.4.13", + "viem": "2.x", + "wagmi": "^2.12.5", "zustand": "^4.5.0" }, "peerDependencies": { diff --git a/skale-network b/skale-network index ed4834e..eb8c44e 160000 --- a/skale-network +++ b/skale-network @@ -1 +1 @@ -Subproject commit ed4834e2d6d61bdb9c5df345d0cd0997b9c93ff7 +Subproject commit eb8c44e8644528fbb6a11df813a7359e469615ee diff --git a/src/components/AddToken.tsx b/src/components/AddToken.tsx index 4892391..15e5f3f 100644 --- a/src/components/AddToken.tsx +++ b/src/components/AddToken.tsx @@ -22,7 +22,7 @@ */ import { useState } from 'react' -import { useWalletClient, useSwitchNetwork } from 'wagmi' +import { useWalletClient, useSwitchChain } from 'wagmi' import { MainnetChain, SChain } from '@skalenetwork/ima-js' import Button from '@mui/material/Button' @@ -43,7 +43,7 @@ export default function AddToken(props: { const [loading, setLoading] = useState(false) const { data: walletClient } = useWalletClient() - const { switchNetworkAsync } = useSwitchNetwork() + const { switchChainAsync } = useSwitchChain() function getIconUrl(token: TokenData) { return `${ICONS_BASE_URL}${token.meta.symbol}.png` @@ -73,7 +73,7 @@ export default function AddToken(props: { await enforceNetwork( chainId, walletClient, - switchNetworkAsync, + switchChainAsync, props.mpc.config.skaleNetwork, props.destChainName ) diff --git a/src/components/ChainApps.tsx b/src/components/ChainApps.tsx index b6cff51..17268e5 100644 --- a/src/components/ChainApps.tsx +++ b/src/components/ChainApps.tsx @@ -67,54 +67,60 @@ export default function ChainApps(props: {
{show ? appButtons : appButtons.length === 4 ? appButtons : appButtons.slice(0, 3)} - {appButtons.length > 4 &&
- } - + {show ? ( + + ) : ( + + )} +

+ Show {show ? 'less' : 'more'} apps +

+
+ + )} ) diff --git a/src/components/CommunityPool.tsx b/src/components/CommunityPool.tsx index 4539361..8cecfb4 100644 --- a/src/components/CommunityPool.tsx +++ b/src/components/CommunityPool.tsx @@ -23,7 +23,7 @@ import React, { useEffect } from 'react' -import { useAccount, useWalletClient, useSwitchNetwork } from 'wagmi' +import { useAccount, useWalletClient, useSwitchChain } from 'wagmi' import Accordion from '@mui/material/Accordion' import AccordionSummary from '@mui/material/AccordionSummary' @@ -57,7 +57,7 @@ import { Collapse } from '@mui/material' export default function CommunityPool() { const { data: walletClient } = useWalletClient() - const { switchNetworkAsync } = useSwitchNetwork() + const { switchChainAsync } = useSwitchChain() const cpData = useCPStore((state) => state.cpData) const loading = useCPStore((state) => state.loading) @@ -144,7 +144,7 @@ export default function CommunityPool() { chainName, cpData.balance, address, - switchNetworkAsync, + switchChainAsync, setLoading, setErrorMessage, async () => { @@ -161,7 +161,7 @@ export default function CommunityPool() { chainName, amount, address, - switchNetworkAsync, + switchChainAsync, setLoading, setErrorMessage, async () => { diff --git a/src/components/MetaportProvider.tsx b/src/components/MetaportProvider.tsx index 17e7d40..83d3502 100644 --- a/src/components/MetaportProvider.tsx +++ b/src/components/MetaportProvider.tsx @@ -22,14 +22,26 @@ import { ReactElement, useEffect } from 'react' -import { RainbowKitProvider, darkTheme, lightTheme } from '@rainbow-me/rainbowkit' -import { configureChains, createConfig, WagmiConfig } from 'wagmi' +import { + RainbowKitProvider, + darkTheme, + lightTheme, + connectorsForWallets +} from '@rainbow-me/rainbowkit' +import { WagmiProvider, createConfig, http } from 'wagmi' import { mainnet, goerli, holesky } from 'wagmi/chains' -import { jsonRpcProvider } from 'wagmi/providers/jsonRpc' -import { connectorsForWallets } from '@rainbow-me/rainbowkit' +import { GetChainsReturnType } from '@wagmi/core' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { PaletteMode } from '@mui/material' -import { injectedWallet, metaMaskWallet, enkryptWallet } from '@rainbow-me/rainbowkit/wallets' +import { + injectedWallet, + metaMaskWallet, + enkryptWallet, + coinbaseWallet, + rainbowWallet, + rabbyWallet +} from '@rainbow-me/rainbowkit/wallets' import { MetaportConfig, ActionStateUpdate } from '../core/interfaces' @@ -38,7 +50,7 @@ import { createTheme, ThemeProvider } from '@mui/material/styles' import '@rainbow-me/rainbowkit/styles.css' -import { constructWagmiChain, getWebSocketUrl } from '../core/wagmi_network' +import { constructWagmiChain } from '../core/wagmi_network' import { getWidgetTheme, getMuiZIndex } from '../core/themes' @@ -56,35 +68,39 @@ export default function MetaportProvider(props: { const skaleChains = props.config.chains.map((chain) => constructWagmiChain(props.config.skaleNetwork, chain) ) - const { chains, webSocketPublicClient } = configureChains( - [mainnet, goerli, holesky, ...skaleChains], - [ - jsonRpcProvider({ - rpc: (chain) => ({ - http: chain.rpcUrls.default.http[0], - webSocket: getWebSocketUrl(chain, props.config.skaleNetwork) - }) - }) - ] - ) - - const wallets = [enkryptWallet({ chains }), injectedWallet({ chains })] - - if (props.config.projectId) { - wallets.push(metaMaskWallet({ chains, projectId: props.config.projectId })) - } - const connectors = connectorsForWallets([ + const connectors = connectorsForWallets( + [ + { + groupName: 'Recommended wallets', + wallets: [ + enkryptWallet, + injectedWallet, + metaMaskWallet, + coinbaseWallet, + rainbowWallet, + rabbyWallet + ] + } + ], { - groupName: 'Supported Wallets', - wallets: wallets + appName: 'SKALE Metaport', + projectId: props.config.projectId } - ]) + ) + const chains: GetChainsReturnType = [mainnet, goerli, holesky, ...skaleChains] const wagmiConfig = createConfig({ - autoConnect: true, + chains, connectors, - publicClient: webSocketPublicClient + transports: { + [mainnet.id]: http(), + [goerli.id]: http(), + [holesky.id]: http(), + ...Object.fromEntries( + skaleChains.map((chain) => [chain.id, http(chain.rpcUrls.default.http[0])]) + ) + } }) const widgetTheme = getWidgetTheme(props.config.theme) @@ -100,7 +116,10 @@ export default function MetaportProvider(props: { useEffect(() => { setOpen(props.config.openOnLoad) - window.addEventListener('metaport_actionStateUpdated', actionStateUpdated, false) + window.addEventListener('metaport_actionStateUpdated', actionStateUpdated as EventListener) + return () => { + window.removeEventListener('metaport_actionStateUpdated', actionStateUpdated as EventListener) + } }, []) useEffect(() => { @@ -148,24 +167,26 @@ export default function MetaportProvider(props: { if (!metaportTheme) return
+ const queryClient = new QueryClient() + return ( - - - - -
{props.children}
-
-
-
-
+ + + + + +
{props.children}
+
+
+
+
+
) } diff --git a/src/components/Stepper/SkStepper.tsx b/src/components/Stepper/SkStepper.tsx index 0e47e81..d5efc8c 100644 --- a/src/components/Stepper/SkStepper.tsx +++ b/src/components/Stepper/SkStepper.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react' -import { useWalletClient, useSwitchNetwork, useAccount } from 'wagmi' +import { useWalletClient, useSwitchChain, useAccount } from 'wagmi' import { useAddRecentTransaction } from '@rainbow-me/rainbowkit' import Box from '@mui/material/Box' @@ -29,7 +29,7 @@ import { SUCCESS_EMOJIS } from '../../core/constants' export default function SkStepper(props: { skaleNetwork: SkaleNetwork }) { const { address } = useAccount() - const { switchNetworkAsync } = useSwitchNetwork() + const { switchChainAsync } = useSwitchChain() const addRecentTransaction = useAddRecentTransaction() const { data: walletClient } = useWalletClient() @@ -124,7 +124,7 @@ export default function SkStepper(props: { skaleNetwork: SkaleNetwork }) { color="primary" size="medium" className={cls(styles.btnAction, cmn.mtop5)} - onClick={() => execute(address, switchNetworkAsync, walletClient)} + onClick={() => execute(address, switchChainAsync, walletClient)} disabled={!!actionDisabled} > {step.btnText} diff --git a/src/components/WrappedTokens.tsx b/src/components/WrappedTokens.tsx index fbf0943..a51f52a 100644 --- a/src/components/WrappedTokens.tsx +++ b/src/components/WrappedTokens.tsx @@ -23,7 +23,7 @@ import React, { useEffect, useState } from 'react' -import { useAccount, useWalletClient, useSwitchNetwork } from 'wagmi' +import { useAccount, useWalletClient, useSwitchChain } from 'wagmi' import Accordion from '@mui/material/Accordion' import AccordionSummary from '@mui/material/AccordionSummary' @@ -49,7 +49,7 @@ import { TokenDataMap } from '../core/interfaces' export default function WrappedTokens() { const { data: walletClient } = useWalletClient() - const { switchNetworkAsync } = useSwitchNetwork() + const { switchChainAsync } = useSwitchChain() const wrappedTokens = useMetaportStore((state) => state.wrappedTokens) const updateWrappedTokenBalances = useMetaportStore((state) => state.updateWrappedTokenBalances) @@ -195,7 +195,7 @@ export default function WrappedTokens() { size="medium" className={cls(styles.btnAction, cmn.mtop5)} onClick={() => - unwrapAll(address, switchNetworkAsync, walletClient, filteredTokens) + unwrapAll(address, switchChainAsync, walletClient, filteredTokens) } > Unwrap all diff --git a/src/core/actions/action.ts b/src/core/actions/action.ts index ebe633e..b4206c7 100644 --- a/src/core/actions/action.ts +++ b/src/core/actions/action.ts @@ -23,7 +23,7 @@ import debug from 'debug' -import { Chain } from '@wagmi/core' +import { UseSwitchChainReturnType } from 'wagmi' import { WalletClient } from 'viem' import { Contract, Provider } from 'ethers' @@ -81,7 +81,7 @@ export class Action { setAmountErrorMessage: React.Dispatch> setBtnText: (btnText: string) => void - _switchNetwork: (chainId: number | bigint) => Promise + _switchChain: UseSwitchChainReturnType['switchChainAsync'] constructor( mpc: MetaportCore, @@ -93,7 +93,7 @@ export class Action { token: TokenData, setAmountErrorMessage: (amountErrorMessage: string) => void, setBtnText: (btnText: string) => void, - switchNetwork: (chainId: number | bigint) => Promise, + switchChain: UseSwitchChainReturnType['switchChainAsync'], walletClient: WalletClient ) { this.mpc = mpc @@ -155,7 +155,7 @@ export class Action { this.setAmountErrorMessage = setAmountErrorMessage this.setBtnText = setBtnText - this._switchNetwork = switchNetwork + this._switchChain = switchChain this.walletClient = walletClient } @@ -192,7 +192,7 @@ export class Action { const updChainId = await enforceNetwork( chainId, this.walletClient, - this._switchNetwork, + this._switchChain, this.mpc.config.skaleNetwork, chainName ?? this.chainName1 ) diff --git a/src/core/community_pool.ts b/src/core/community_pool.ts index 6df70b0..388927b 100644 --- a/src/core/community_pool.ts +++ b/src/core/community_pool.ts @@ -26,7 +26,7 @@ import { ethers } from 'ethers' import { MainnetChain, SChain } from '@skalenetwork/ima-js' import { WalletClient } from 'viem' -import { Chain } from '@wagmi/core' +import { type UseSwitchChainReturnType } from 'wagmi' import { CommunityPoolData } from './interfaces' import { fromWei, toWei } from './convertation' @@ -100,7 +100,6 @@ export async function getCommunityPoolData( recommendedRechargeAmount: recommendedAmount, originalRecommendedRechargeAmount: rraWei } - // log('communityPoolData:', communityPoolData) return communityPoolData } @@ -110,7 +109,7 @@ export async function withdraw( chainName: string, amount: bigint, address: `0x${string}`, - switchNetwork: (chainId: number | bigint) => Promise, + switchChain: UseSwitchChainReturnType['switchChainAsync'], setLoading: (loading: string | false) => void, setErrorMessage: (errorMessage: dataclasses.ErrorMessage) => void, errorMessageClosedFallback: () => void @@ -122,7 +121,7 @@ export async function withdraw( await enforceNetwork( chainId, walletClient, - switchNetwork, + switchChain, mpc.config.skaleNetwork, MAINNET_CHAIN_NAME ) @@ -149,7 +148,7 @@ export async function recharge( chainName: string, amount: string, address: `0x${string}`, - switchNetwork: (chainId: number | bigint) => Promise, + switchChain: UseSwitchChainReturnType['switchChainAsync'], setLoading: (loading: string | false) => void, setErrorMessage: (errorMessage: dataclasses.ErrorMessage) => void, errorMessageClosedFallback: () => void @@ -163,7 +162,7 @@ export async function recharge( await enforceNetwork( chainId, walletClient, - switchNetwork, + switchChain, mpc.config.skaleNetwork, MAINNET_CHAIN_NAME ) diff --git a/src/core/ethers.ts b/src/core/ethers.ts index b748877..4ee725e 100644 --- a/src/core/ethers.ts +++ b/src/core/ethers.ts @@ -1,12 +1,39 @@ +/** + * @license + * SKALE Metaport + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ +/** + * @file ethers.ts + * @copyright SKALE Labs 2024-Present + */ + import * as React from 'react' -import { type WalletClient, useWalletClient } from 'wagmi' + +import { + type UseWalletClientReturnType, + useWalletClient, + usePublicClient, + type UsePublicClientReturnType +} from 'wagmi' import { BrowserProvider, JsonRpcSigner, Eip1193Provider } from 'ethers' -import { type PublicClient, usePublicClient } from 'wagmi' import { FallbackProvider, JsonRpcProvider } from 'ethers' import { type HttpTransport } from 'viem' -export function walletClientToSigner(walletClient: WalletClient) { +export function walletClientToSigner(walletClient: UseWalletClientReturnType['data']) { const { account, transport } = walletClient const provider = new BrowserProvider(transport as Eip1193Provider, 'any') const signer = new JsonRpcSigner(provider, account.address) @@ -22,7 +49,7 @@ export function useEthersSigner({ chainId }: { chainId?: number } = {}) { ) } -export function publicClientToProvider(publicClient: PublicClient) { +export function publicClientToProvider(publicClient: UsePublicClientReturnType) { const { chain, transport } = publicClient const network = { chainId: chain.id, diff --git a/src/core/network.ts b/src/core/network.ts index 8beccf8..99f5324 100644 --- a/src/core/network.ts +++ b/src/core/network.ts @@ -26,8 +26,8 @@ import { MainnetChain, SChain, TimeoutException } from '@skalenetwork/ima-js' import { JsonRpcProvider } from 'ethers' import { WalletClient } from 'viem' -import { Chain } from '@wagmi/core' import { holesky } from '@wagmi/core/chains' +import { type UseSwitchChainReturnType } from 'wagmi' import proxyEndpoints from '../metadata/proxy.json' import { MAINNET_CHAIN_NAME, DEFAULT_ITERATIONS, DEFAULT_SLEEP } from './constants' @@ -143,9 +143,9 @@ async function waitForNetworkChange( async function _networkSwitch( chainId: number | bigint, currentChainId: number | bigint, - switchNetwork: (chainId: number | undefined) => Promise + switchChain: UseSwitchChainReturnType['switchChainAsync'] ): Promise { - const chain = await switchNetwork(Number(chainId)) + const chain = await switchChain({ chainId: Number(chainId) }) if (!chain) { throw new Error(`Failed to switch from ${currentChainId} to ${chainId} `) } @@ -154,7 +154,7 @@ async function _networkSwitch( export async function enforceNetwork( chainId: bigint, walletClient: any, - switchNetwork: (chainId: number | undefined) => Promise, + switchChain: UseSwitchChainReturnType['switchChainAsync'], skaleNetwork: SkaleNetwork, chainName: string ): Promise { @@ -173,10 +173,10 @@ export async function enforceNetwork( } try { // tmp fix for coinbase wallet - _networkSwitch(chainId, currentChainId, switchNetwork) + _networkSwitch(chainId, currentChainId, switchChain) } catch (e) { await sleep(DEFAULT_SLEEP) - _networkSwitch(chainId, currentChainId, switchNetwork) + _networkSwitch(chainId, currentChainId, switchChain) } await waitForNetworkChange(_walletClient, currentChainId, chainId) log(`Network switched to ${chainId}...`) diff --git a/src/core/wagmi_network.ts b/src/core/wagmi_network.ts index 2a2f7d4..70eede4 100644 --- a/src/core/wagmi_network.ts +++ b/src/core/wagmi_network.ts @@ -21,13 +21,12 @@ * @copyright SKALE Labs 2023-Present */ -import { Chain } from 'wagmi' +import { Chain } from 'wagmi/chains' import { getSChainEndpoint } from './network' import { getExplorerUrl } from './explorer' import { getChainAlias } from './metadata' import { getChainId } from './chain_id' -import { MAINNET_WS_ENDPOINTS } from './constants' import { SkaleNetwork } from './interfaces' export function constructWagmiChain(network: SkaleNetwork, chainName: string): Chain { @@ -39,7 +38,6 @@ export function constructWagmiChain(network: SkaleNetwork, chainName: string): C return { id: chainId, name: name, - network: `skale-${chainName}`, nativeCurrency: { decimals: 18, name: 'sFUEL', @@ -56,9 +54,3 @@ export function constructWagmiChain(network: SkaleNetwork, chainName: string): C contracts: {} } as const satisfies Chain } - -export function getWebSocketUrl(chain: Chain, skaleNetwork: SkaleNetwork): string { - return chain.rpcUrls.default.webSocket - ? chain.rpcUrls.default.webSocket[0] - : MAINNET_WS_ENDPOINTS[skaleNetwork] -} diff --git a/src/index.ts b/src/index.ts index ae5b568..b2e76ff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -49,7 +49,7 @@ import { getWidgetTheme as getMetaportTheme } from './core/themes' import { useAccount as useWagmiAccount, useWalletClient as useWagmiWalletClient, - useSwitchNetwork as useWagmiSwitchNetwork + useSwitchChain as useWagmiSwitchNetwork } from 'wagmi' import { ConnectButton as RainbowConnectButton } from '@rainbow-me/rainbowkit' diff --git a/src/metadata/metaportConfigTestnet.ts b/src/metadata/metaportConfigTestnet.ts index b1b33ed..a0bfb37 100644 --- a/src/metadata/metaportConfigTestnet.ts +++ b/src/metadata/metaportConfigTestnet.ts @@ -129,7 +129,7 @@ export const METAPORT_CONFIG: interfaces.MetaportConfig = { hub: 'juicy-low-small-testnet' } } - }, + } // wbtc: { // address: '0x', // chains: { @@ -147,10 +147,8 @@ export const METAPORT_CONFIG: interfaces.MetaportConfig = { // } // } }, - erc721meta: { - }, - erc1155: { - } + erc721meta: {}, + erc1155: {} }, 'giant-half-dual-testnet': { // Calypso connections @@ -202,7 +200,8 @@ export const METAPORT_CONFIG: interfaces.MetaportConfig = { } } }, - 'lanky-ill-funny-testnet': { // nebula connections + 'lanky-ill-funny-testnet': { + // nebula connections eth: { eth: { address: '0x319f0eeb1a1e59943ebe44f766dbb592db664cf0', @@ -251,7 +250,8 @@ export const METAPORT_CONFIG: interfaces.MetaportConfig = { } } }, - 'aware-fake-trim-testnet': { // titan connections + 'aware-fake-trim-testnet': { + // titan connections erc20: { usdc: { address: '0x10a30e73ab2da5328fc09b06443dde3e656e82f4', @@ -348,7 +348,7 @@ export const METAPORT_CONFIG: interfaces.MetaportConfig = { wrapper: '0xa6be26f2914a17fc4e8d21a1ce2ec4079eeb990c' } } - }, + } // wbtc: { // address: '0x', // chains: { @@ -372,4 +372,4 @@ export const METAPORT_CONFIG: interfaces.MetaportConfig = { mode: 'dark', vibrant: true } -} \ No newline at end of file +} diff --git a/src/store/MetaportState.ts b/src/store/MetaportState.ts index bc5e7f1..a4b3e6b 100644 --- a/src/store/MetaportState.ts +++ b/src/store/MetaportState.ts @@ -23,6 +23,7 @@ import { Contract } from 'ethers' import { WalletClient } from 'viem' +import { UseSwitchChainReturnType } from 'wagmi' import { MainnetChain, SChain } from '@skalenetwork/ima-js' import MetaportCore from '../core/metaport' @@ -47,13 +48,13 @@ export interface MetaportState { execute: ( address: string, - switchNetwork: (chainId: number) => void, + switchChain: UseSwitchChainReturnType['switchChainAsync'], walletClient: WalletClient ) => void unwrapAll: ( address: string, - switchNetwork: (chainId: number) => void, + switchChain: UseSwitchChainReturnType['switchChainAsync'], walletClient: WalletClient, tokens: interfaces.TokenDataMap ) => void diff --git a/src/store/MetaportStore.ts b/src/store/MetaportStore.ts index 571acb9..d2ef645 100644 --- a/src/store/MetaportStore.ts +++ b/src/store/MetaportStore.ts @@ -64,7 +64,7 @@ export const useMetaportStore = create()((set, get) => ({ unwrapAll: async ( address: `0x${string}`, - switchNetwork: any, + switchChain: any, walletClient: WalletClient, tokens: interfaces.TokenDataMap ) => { @@ -82,7 +82,7 @@ export const useMetaportStore = create()((set, get) => ({ tokens[key], get().setAmountErrorMessage, get().setBtnText, - switchNetwork, + switchChain, walletClient ).execute() } @@ -98,7 +98,7 @@ export const useMetaportStore = create()((set, get) => ({ } }, - execute: async (address: `0x${string}`, switchNetwork: any, walletClient: WalletClient) => { + execute: async (address: `0x${string}`, switchChain: any, walletClient: WalletClient) => { log('Running execute') if (get().stepsMetadata[get().currentStep]) { set({ @@ -118,7 +118,7 @@ export const useMetaportStore = create()((set, get) => ({ get().token, get().setAmountErrorMessage, get().setBtnText, - switchNetwork, + switchChain, walletClient ).execute() } catch (err) {