diff --git a/package.json b/package.json index e9a4dcc6..188f28cc 100644 --- a/package.json +++ b/package.json @@ -19,12 +19,13 @@ "@mui/lab": "5.0.0-alpha.134", "@mui/material": "^5.15.14", "@mui/x-date-pickers": "^6.19.5", - "@polkadot/api": "^10.12.6", - "@polkadot/api-contract": "^10.13.1", - "@polkadot/extension-inject": "^0.46.4", - "@polkadot/types": "^10.12.6", - "@polkadot/util": "^12.3.1", - "@scio-labs/use-inkathon": "^0.0.1-alpha.44", + "@polkadot/api": "latest", + "@polkadot/api-contract": "latest", + "@polkadot/extension-dapp": "latest", + "@polkadot/extension-inject": "latest", + "@polkadot/types": "latest", + "@polkadot/ui-keyring": "latest", + "@polkadot/util": "latest", "@types/humanize-duration": "^3.27.3", "clsx": "^1.1.1", "coretime-utils": "^0.2.8", diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index 7f033806..4dd79c1f 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -7,24 +7,27 @@ import { ListItemButton, useTheme, } from '@mui/material'; -import { useInkathon } from '@scio-labs/use-inkathon'; import React, { useState } from 'react'; +import { useAccounts } from '@/contexts/account'; + import styles from './index.module.scss'; import { ActionButton } from '../Elements'; -import { WalletModal } from '../Modals/WalletConnect'; export const Header = () => { const theme = useTheme(); + const { + state: { accounts, activeAccount }, + setActiveAccount, + disconnectWallet, + connectWallet, + } = useAccounts(); - const { activeAccount, disconnect, accounts, setActiveAccount } = - useInkathon(); const [accountsOpen, openAccounts] = useState(false); - const [walletModalOpen, openWalletModal] = useState(false); const onDisconnect = () => { openAccounts(false); - disconnect && disconnect(); + disconnectWallet(); }; return ( @@ -49,7 +52,7 @@ export const Header = () => { borderRadius: 4, }} > - {activeAccount.name} + {activeAccount.meta.name} )} @@ -70,7 +73,7 @@ export const Header = () => { { - setActiveAccount && setActiveAccount(account); + setActiveAccount(account); openAccounts(false); }} sx={{ @@ -78,7 +81,7 @@ export const Header = () => { background: theme.palette.grey['100'], }} > - {account.name} + {account.meta.name} ) )} @@ -97,16 +100,12 @@ export const Header = () => { ) : ( openWalletModal(true)} + onClick={() => connectWallet()} label='Connect Wallet' /> )} - openWalletModal(false)} - /> ); }; diff --git a/src/components/Modals/Interlace/index.tsx b/src/components/Modals/Interlace/index.tsx index e05a75e7..9027dd7f 100644 --- a/src/components/Modals/Interlace/index.tsx +++ b/src/components/Modals/Interlace/index.tsx @@ -11,12 +11,12 @@ import { Typography, useTheme, } from '@mui/material'; -import { useInkathon } from '@scio-labs/use-inkathon'; import { CoreMask } from 'coretime-utils'; import { useEffect, useState } from 'react'; import { RegionCard } from '@/components/Elements'; +import { useAccounts } from '@/contexts/account'; import { useCoretimeApi } from '@/contexts/apis'; import { useRegions } from '@/contexts/regions'; import { useToast } from '@/contexts/toast'; @@ -36,7 +36,9 @@ export const InterlaceModal = ({ regionMetadata, }: InterlaceModalProps) => { const theme = useTheme(); - const { activeAccount, activeSigner } = useInkathon(); + const { + state: { activeAccount, activeSigner }, + } = useAccounts(); const { state: { api }, diff --git a/src/components/Modals/Partition/index.tsx b/src/components/Modals/Partition/index.tsx index eb6e8ab2..15cf3318 100644 --- a/src/components/Modals/Partition/index.tsx +++ b/src/components/Modals/Partition/index.tsx @@ -11,11 +11,11 @@ import { Typography, useTheme, } from '@mui/material'; -import { useInkathon } from '@scio-labs/use-inkathon'; import { useEffect, useState } from 'react'; import { RegionCard } from '@/components/Elements'; +import { useAccounts } from '@/contexts/account'; import { useCoretimeApi } from '@/contexts/apis'; import { useCommon } from '@/contexts/common'; import { useRegions } from '@/contexts/regions'; @@ -59,7 +59,9 @@ export const PartitionModal = ({ }, ]; - const { activeSigner, activeAccount } = useInkathon(); + const { + state: { activeSigner, activeAccount }, + } = useAccounts(); const theme = useTheme(); const { diff --git a/src/components/Modals/Purchase/index.tsx b/src/components/Modals/Purchase/index.tsx index e601d5cd..fae3db81 100644 --- a/src/components/Modals/Purchase/index.tsx +++ b/src/components/Modals/Purchase/index.tsx @@ -6,11 +6,11 @@ import { DialogContent, Stack, } from '@mui/material'; -import { useInkathon } from '@scio-labs/use-inkathon'; import { useState } from 'react'; import { ListingCard } from '@/components/Elements/ListingCard'; +import { useAccounts } from '@/contexts/account'; import { useToast } from '@/contexts/toast'; import { Listing } from '@/models'; @@ -25,22 +25,23 @@ export const PurchaseModal = ({ onClose, listing, }: PurchaseModalProps) => { - const { activeAccount, api } = useInkathon(); + const { + state: { activeAccount }, + } = useAccounts(); const { toastError, toastSuccess } = useToast(); const [working, setWorking] = useState(false); const purchaseRegion = async () => { - if (!api || !activeAccount) { + if (!activeAccount) { return; } try { setWorking(true); - // TODO - + // TODO: toastSuccess(`Successfully purchased region from sale.`); onClose(); setWorking(false); diff --git a/src/components/Modals/Sell/index.tsx b/src/components/Modals/Sell/index.tsx index 638097e6..bc66c888 100644 --- a/src/components/Modals/Sell/index.tsx +++ b/src/components/Modals/Sell/index.tsx @@ -8,13 +8,13 @@ import { Stack, Typography, } from '@mui/material'; -import { useInkathon } from '@scio-labs/use-inkathon'; import { Region } from 'coretime-utils'; import { useEffect, useState } from 'react'; import { AmountInput, RegionCard } from '@/components/Elements'; import { RecipientSelector } from '@/components/Elements/Selectors/RecipientSelector'; +import { useAccounts } from '@/contexts/account'; import { useCoretimeApi } from '@/contexts/apis'; import { useRegions } from '@/contexts/regions'; import { useToast } from '@/contexts/toast'; @@ -31,7 +31,9 @@ export const SellModal = ({ onClose, regionMetadata, }: SellModalProps) => { - const { activeAccount, api } = useInkathon(); + const { + state: { activeAccount }, + } = useAccounts(); const { state: { symbol }, } = useCoretimeApi(); @@ -55,14 +57,14 @@ export const SellModal = ({ }; const approveXcRegion = async (_region: Region) => { - if (!api || !activeAccount) { + if (!activeAccount) { return; } try { setWorking(true); - // TODO + // TODO: toastSuccess(`Successfully approved region to the market.`); setWorking(false); @@ -77,7 +79,7 @@ export const SellModal = ({ }; const listRegion = async (_region: Region) => { - if (!api || !activeAccount) { + if (!activeAccount) { return; } diff --git a/src/components/Modals/TaskAssign/index.tsx b/src/components/Modals/TaskAssign/index.tsx index e956bdc3..723996f2 100644 --- a/src/components/Modals/TaskAssign/index.tsx +++ b/src/components/Modals/TaskAssign/index.tsx @@ -12,11 +12,11 @@ import { TextField, Typography, } from '@mui/material'; -import { useInkathon } from '@scio-labs/use-inkathon'; import { useEffect, useState } from 'react'; import { RegionCard } from '@/components/Elements'; +import { useAccounts } from '@/contexts/account'; import { useCoretimeApi, useRelayApi } from '@/contexts/apis'; import { useRegions } from '@/contexts/regions'; import { useTasks } from '@/contexts/tasks'; @@ -34,7 +34,9 @@ export const TaskAssignModal = ({ onClose, regionMetadata, }: TaskAssignModalProps) => { - const { activeAccount, activeSigner } = useInkathon(); + const { + state: { activeAccount, activeSigner }, + } = useAccounts(); const { paraIds } = useRelayApi(); const { diff --git a/src/components/Modals/Transfer/index.tsx b/src/components/Modals/Transfer/index.tsx index 0ecdaba3..3f73915b 100644 --- a/src/components/Modals/Transfer/index.tsx +++ b/src/components/Modals/Transfer/index.tsx @@ -9,7 +9,6 @@ import { TextField, Typography, } from '@mui/material'; -import { useInkathon } from '@scio-labs/use-inkathon'; import { Region } from 'coretime-utils'; import { useEffect, useState } from 'react'; @@ -17,6 +16,7 @@ import { transferRegionOnCoretimeChain } from '@/utils/native/transfer'; import { RegionCard } from '@/components/Elements'; +import { useAccounts } from '@/contexts/account'; import { useCoretimeApi } from '@/contexts/apis'; import { useRegions } from '@/contexts/regions'; import { useToast } from '@/contexts/toast'; @@ -33,7 +33,9 @@ export const TransferModal = ({ onClose, regionMetadata, }: TransferModalProps) => { - const { activeAccount, activeSigner } = useInkathon(); + const { + state: { activeAccount, activeSigner }, + } = useAccounts(); const { fetchRegions } = useRegions(); const { toastError, toastInfo, toastSuccess } = useToast(); diff --git a/src/components/Modals/Unlist/index.tsx b/src/components/Modals/Unlist/index.tsx index 2a201a8e..f88372bf 100644 --- a/src/components/Modals/Unlist/index.tsx +++ b/src/components/Modals/Unlist/index.tsx @@ -6,12 +6,12 @@ import { DialogContent, Stack, } from '@mui/material'; -import { useInkathon } from '@scio-labs/use-inkathon'; import { Region } from 'coretime-utils'; import { useState } from 'react'; import { RegionCard } from '@/components/Elements'; +import { useAccounts } from '@/contexts/account'; import { useMarket } from '@/contexts/market'; import { useRegions } from '@/contexts/regions'; import { useToast } from '@/contexts/toast'; @@ -28,7 +28,9 @@ export const UnlistModal = ({ onClose, regionMetadata, }: UnlistModalProps) => { - const { activeAccount, api } = useInkathon(); + const { + state: { activeAccount }, + } = useAccounts(); const { fetchRegions } = useRegions(); const { fetchMarket } = useMarket(); @@ -37,7 +39,7 @@ export const UnlistModal = ({ const [working, setWorking] = useState(false); const unlistRegion = async (_region: Region) => { - if (!api || !activeAccount) { + if (!activeAccount) { return; } diff --git a/src/components/Modals/WalletConnect/index.module.scss b/src/components/Modals/WalletConnect/index.module.scss deleted file mode 100644 index 75fd716f..00000000 --- a/src/components/Modals/WalletConnect/index.module.scss +++ /dev/null @@ -1,3 +0,0 @@ -.logo { - margin: 0.5em; -} diff --git a/src/components/Modals/WalletConnect/index.tsx b/src/components/Modals/WalletConnect/index.tsx deleted file mode 100644 index 3f210b86..00000000 --- a/src/components/Modals/WalletConnect/index.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { - Button, - Dialog, - DialogActions, - DialogContent, - DialogTitle, - List, - ListItemButton, -} from '@mui/material'; -import { isWalletInstalled, SubstrateWallet } from '@scio-labs/use-inkathon'; -import { allSubstrateWallets, useInkathon } from '@scio-labs/use-inkathon'; -import Image from 'next/image'; - -import styles from './index.module.scss'; - -interface WalletModalProps { - open: boolean; - onClose: () => void; -} - -export const WalletModal = (props: WalletModalProps) => { - const { connect } = useInkathon(); - - const onConnect = (wallet: SubstrateWallet) => { - connect?.(undefined, wallet); - props.onClose(); - }; - - return ( - - Choose your wallet extension - - - {allSubstrateWallets.map((wallet, index) => ( - onConnect(wallet)} - disabled={!isWalletInstalled(wallet)} - > - logo - {wallet.name} - - ))} - - - - - - - ); -}; diff --git a/src/components/Modals/index.ts b/src/components/Modals/index.ts index 79dfc91d..32dfdf40 100644 --- a/src/components/Modals/index.ts +++ b/src/components/Modals/index.ts @@ -5,4 +5,3 @@ export * from './Sell'; export * from './TaskAssign'; export * from './Transfer'; export * from './Unlist'; -export * from './WalletConnect'; diff --git a/src/contexts/account/index.tsx b/src/contexts/account/index.tsx new file mode 100644 index 00000000..450d2398 --- /dev/null +++ b/src/contexts/account/index.tsx @@ -0,0 +1,133 @@ +import type { Signer } from '@polkadot/api/types'; +import { + web3Accounts, + web3Enable, + web3FromSource, +} from '@polkadot/extension-dapp'; +import { InjectedAccountWithMeta } from '@polkadot/extension-inject/types'; +import React, { createContext, useContext, useEffect, useReducer } from 'react'; + +export enum KeyringState { + // eslint-disable-next-line no-unused-vars + DISCONNECTED, + // eslint-disable-next-line no-unused-vars + LOADING, + // eslint-disable-next-line no-unused-vars + READY, + // eslint-disable-next-line no-unused-vars + ERROR, +} + +type State = { + status: KeyringState; + accounts: InjectedAccountWithMeta[]; + activeAccount: InjectedAccountWithMeta | null; + activeSigner: Signer | null; +}; + +const initialState: State = { + status: KeyringState.LOADING, + accounts: [], + activeAccount: null, + activeSigner: null, +}; + +interface Props { + children: React.ReactNode; +} + +const reducer = (state: State, action: any) => { + switch (action.type) { + case 'LOAD_KEYRING': + return { ...state, keyringState: KeyringState.LOADING }; + case 'SET_KEYRING': + return { + ...state, + keyring: action.payload, + keyringState: KeyringState.READY, + }; + case 'KEYRING_ERROR': + return { ...state, keyring: null, keyringState: KeyringState.ERROR }; + case 'SET_ACCOUNTS': + return { ...state, accounts: action.payload }; + case 'SET_ACTIVE_ACCOUNT': + return { ...state, activeAccount: action.payload }; + case 'SET_ACTIVE_SIGNER': + return { ...state, activeSigner: action.payload } as State; + case 'DISCONNECT': + return { + status: KeyringState.DISCONNECTED, + accounts: [], + activeAccount: null, + activeSigner: null, + } as State; + default: + throw new Error(`Unknown type: ${action.type}`); + } +}; + +interface AccountData { + state: State; + setActiveAccount: (_acct: InjectedAccountWithMeta) => void; + connectWallet: () => void; + disconnectWallet: () => void; +} + +const defaultAccountData: AccountData = { + state: initialState, + setActiveAccount: (_acct: InjectedAccountWithMeta) => { + /** */ + }, + connectWallet: () => { + /** */ + }, + disconnectWallet: () => { + /** */ + }, +}; + +const AccountDataContext = createContext(defaultAccountData); + +const AccountProvider = ({ children }: Props) => { + const [state, dispatch] = useReducer(reducer, initialState); + + const setActiveAccount = (acct: any) => { + dispatch({ type: 'SET_CURRENT_ACCOUNT', payload: acct }); + }; + + const connectWallet = () => { + dispatch({ type: 'LOAD_KEYRING' }); + const asyncLoadAccounts = async () => { + try { + await web3Enable('Corehub'); + const accounts: InjectedAccountWithMeta[] = await web3Accounts(); + dispatch({ type: 'SET_ACCOUNTS', payload: accounts }); + } catch (e) { + dispatch({ type: 'KEYRING_ERROR' }); + } + }; + asyncLoadAccounts(); + }; + + useEffect(() => { + const getInjector = async () => { + if (!state.activeAccount) return; + const injector = await web3FromSource(state.activeAccount.meta.source); + dispatch({ type: 'SET_ACTIVE_SIGNER', payload: injector.signer }); + }; + getInjector(); + }, [state.activeAccount]); + + const disconnectWallet = () => dispatch({ type: 'DISCONNECT' }); + return ( + + {children} + + ); +}; + +const useAccounts = () => useContext(AccountDataContext); + +export { AccountProvider, useAccounts }; diff --git a/src/contexts/regions/index.tsx b/src/contexts/regions/index.tsx index 6a3a3d65..f6bdbb63 100644 --- a/src/contexts/regions/index.tsx +++ b/src/contexts/regions/index.tsx @@ -1,4 +1,3 @@ -import { useInkathon } from '@scio-labs/use-inkathon'; import { CoreIndex, CoreMask, Region, RegionId } from 'coretime-utils'; import React, { createContext, @@ -11,6 +10,7 @@ import React, { import { RegionLocation, RegionMetadata } from '@/models'; import * as NativeRegions from './native'; +import { useAccounts } from '../account'; import { useCoretimeApi } from '../apis'; import { useCommon } from '../common'; import { useTasks } from '../tasks'; @@ -46,7 +46,9 @@ const RegionDataProvider = ({ children }: Props) => { const { state: { api: coretimeApi }, } = useCoretimeApi(); - const { api, activeAccount } = useInkathon(); + const { + state: { activeAccount }, + } = useAccounts(); const { fetchWorkplan, fetchRegionWorkload } = useTasks(); @@ -84,7 +86,7 @@ const RegionDataProvider = ({ children }: Props) => { ) continue; - const rawId = region.getEncodedRegionId(api).toString(); + const rawId = region.getEncodedRegionId(coretimeApi).toString(); const location = RegionLocation.CORETIME_CHAIN; const name = @@ -98,7 +100,7 @@ const RegionDataProvider = ({ children }: Props) => { _regions.push( RegionMetadata.construct( context, - region.getEncodedRegionId(api), + region.getEncodedRegionId(coretimeApi), region, name, location, @@ -113,7 +115,6 @@ const RegionDataProvider = ({ children }: Props) => { activeAccount, context, coretimeApi, - api, fetchWorkplan, _getTaskFromWorkloadId, ]); diff --git a/src/contexts/tasks/index.tsx b/src/contexts/tasks/index.tsx index 52d69a6b..6f438b43 100644 --- a/src/contexts/tasks/index.tsx +++ b/src/contexts/tasks/index.tsx @@ -1,4 +1,3 @@ -import { useInkathon } from '@scio-labs/use-inkathon'; import { CoreIndex, CoreMask, Region } from 'coretime-utils'; import React, { createContext, useContext, useEffect, useState } from 'react'; @@ -51,7 +50,6 @@ const TaskDataProvider = ({ children }: Props) => { const { state: { api: coretimeApi, apiState: coretimeApiState }, } = useCoretimeApi(); - const { api } = useInkathon(); const STORAGE_ITEM_KEY = 'tasks'; @@ -85,7 +83,7 @@ const TaskDataProvider = ({ children }: Props) => { { end: 0, owner: '', paid: null }, 0 ); - tasks[region.getEncodedRegionId(api).toString()] = taskId + tasks[region.getEncodedRegionId(coretimeApi).toString()] = taskId ? parseHNString(taskId) : null; }); diff --git a/src/hooks/balance.tsx b/src/hooks/balance.tsx index de81e325..9606ecc4 100644 --- a/src/hooks/balance.tsx +++ b/src/hooks/balance.tsx @@ -1,9 +1,9 @@ import { ApiPromise } from '@polkadot/api'; -import { useInkathon } from '@scio-labs/use-inkathon'; import { useCallback, useEffect, useState } from 'react'; import { parseHNString } from '@/utils/functions'; +import { useAccounts } from '@/contexts/account'; import { useCoretimeApi, useRelayApi } from '@/contexts/apis'; import { ApiState } from '@/contexts/apis/types'; import { useToast } from '@/contexts/toast'; @@ -17,7 +17,9 @@ const useBalance = () => { state: { api: relayApi, apiState: relayApiState }, } = useRelayApi(); - const { activeAccount } = useInkathon(); + const { + state: { activeAccount }, + } = useAccounts(); const [coretimeBalance, setCoretimeBalance] = useState(0); const [relayBalance, setRelayBalance] = useState(0); diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 8eb733b7..e038f765 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,12 +1,9 @@ import { CacheProvider, EmotionCache } from '@emotion/react'; import CssBaseline from '@mui/material/CssBaseline'; import { ThemeProvider } from '@mui/material/styles'; -import { UseInkathonProvider } from '@scio-labs/use-inkathon'; -import { Id } from 'coretime-utils'; import { NextPage } from 'next'; import { AppProps } from 'next/app'; import Head from 'next/head'; -import { useRouter } from 'next/router'; import * as React from 'react'; import '../../styles/global.scss'; @@ -15,14 +12,11 @@ import theme from '@/utils/muiTheme'; import { Layout } from '@/components'; +import { AccountProvider } from '@/contexts/account'; import { CoretimeApiContextProvider, RelayApiContextProvider, } from '@/contexts/apis'; -import { - WS_KUSAMA_CORETIME_CHAIN, - WS_ROCOCO_CORETIME_CHAIN, -} from '@/contexts/apis/consts'; import { ContextDataProvider } from '@/contexts/common'; import { MarketProvider } from '@/contexts/market'; import { NetworkProvider } from '@/contexts/network'; @@ -42,25 +36,9 @@ interface MyAppProps extends AppProps { } export default function MyApp(props: MyAppProps) { - const router = useRouter(); - const { network } = router.query; - const { Component, emotionCache = clientSideEmotionCache, pageProps } = props; const getLayout = Component.getLayout ?? ((page) => {page}); - const getUrl = (network: any): string => { - if (!network || network === 'rococo') { - return WS_ROCOCO_CORETIME_CHAIN; - } else if (network === 'kusama') { - return WS_KUSAMA_CORETIME_CHAIN; - } else { - /* eslint-disable no-console */ - console.error(`Network: ${network} not recognized`); - // default to rococo. - return WS_ROCOCO_CORETIME_CHAIN; - } - }; - return ( @@ -71,18 +49,9 @@ export default function MyApp(props: MyAppProps) { - - - + + + @@ -94,9 +63,9 @@ export default function MyApp(props: MyAppProps) { - - - + + + diff --git a/src/pages/purchase.tsx b/src/pages/purchase.tsx index 97b53c05..76ac42ed 100644 --- a/src/pages/purchase.tsx +++ b/src/pages/purchase.tsx @@ -1,5 +1,4 @@ import { Box, Button, Typography, useTheme } from '@mui/material'; -import { useInkathon } from '@scio-labs/use-inkathon'; import TimeAgo from 'javascript-time-ago'; import en from 'javascript-time-ago/locale/en.json'; import Link from 'next/link'; @@ -13,6 +12,7 @@ import { sendTx } from '@/utils/functions'; import { CoreDetailsPanel, ProgressButton, SaleInfoPanel } from '@/components'; import Balance from '@/components/Elements/Balance'; +import { useAccounts } from '@/contexts/account'; import { useCoretimeApi } from '@/contexts/apis'; import { ApiState } from '@/contexts/apis/types'; import { useRegions } from '@/contexts/regions'; @@ -26,7 +26,9 @@ const Purchase = () => { TimeAgo.addLocale(en); // Create formatter (English). - const { activeSigner, activeAccount } = useInkathon(); + const { + state: { activeSigner, activeAccount }, + } = useAccounts(); const { toastError, toastSuccess, toastInfo } = useToast(); const { saleInfo, loading } = useSaleInfo(); diff --git a/src/pages/transfer.tsx b/src/pages/transfer.tsx index 91c34cd0..6493f294 100644 --- a/src/pages/transfer.tsx +++ b/src/pages/transfer.tsx @@ -9,7 +9,6 @@ import { Typography, } from '@mui/material'; import { Keyring } from '@polkadot/api'; -import { useInkathon } from '@scio-labs/use-inkathon'; import { Region } from 'coretime-utils'; import { useEffect, useState } from 'react'; @@ -34,6 +33,7 @@ import { import Balance from '@/components/Elements/Balance'; import AssetSelector from '@/components/Elements/Selectors/AssetSelector'; +import { useAccounts } from '@/contexts/account'; import { useCoretimeApi, useRelayApi } from '@/contexts/apis'; import { ApiState } from '@/contexts/apis/types'; import { useRegions } from '@/contexts/regions'; @@ -47,7 +47,9 @@ import { } from '@/models'; const TransferPage = () => { - const { activeAccount, activeSigner } = useInkathon(); + const { + state: { activeAccount, activeSigner }, + } = useAccounts(); const { toastError, toastInfo, toastWarning, toastSuccess } = useToast(); const {