From 5e64ab2b282b308db11f2a0a3b92d07666db0c61 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 5 Dec 2024 03:01:45 -0500 Subject: [PATCH 01/48] Refactor network configuration handling and introduce Zustand store for state management --- .../NetworkConfig/NetworkConfigProvider.tsx | 65 +++++-------------- .../NetworkConfig/useNetworkConfigStore.ts | 31 +++++++++ src/providers/Providers.tsx | 25 ++++--- 3 files changed, 57 insertions(+), 64 deletions(-) create mode 100644 src/providers/NetworkConfig/useNetworkConfigStore.ts diff --git a/src/providers/NetworkConfig/NetworkConfigProvider.tsx b/src/providers/NetworkConfig/NetworkConfigProvider.tsx index 73d0de8817..2ce3cc619a 100644 --- a/src/providers/NetworkConfig/NetworkConfigProvider.tsx +++ b/src/providers/NetworkConfig/NetworkConfigProvider.tsx @@ -1,54 +1,19 @@ -import { createContext, ReactNode, useContext, useEffect, useState } from 'react'; +import { useEffect } from 'react'; import { useChainId } from 'wagmi'; -import { NetworkConfig } from '../../types/network'; +import { useNetworkConfigStore } from './useNetworkConfigStore'; -import { networks } from './networks'; - -type NetworkConfigContextType = { - currentConfig: NetworkConfig; - getConfigByChainId: (chainId: number) => NetworkConfig; -}; - -export const NetworkConfigContext = createContext( - {} as NetworkConfigContextType, -); - -export const useNetworkConfig = (chainId?: number) => { - const context = useContext(NetworkConfigContext); - if (chainId) { - return context.getConfigByChainId(chainId); - } - return context.currentConfig; -}; - -export const supportedNetworks = Object.values(networks).sort((a, b) => a.order - b.order); -export const moralisSupportedChainIds = supportedNetworks - .filter(network => network.moralis.chainSupported) - .map(network => network.chain.id); - -export const getNetworkConfig = (chainId: number) => { - const foundChain = supportedNetworks.find(network => network.chain.id === chainId); - if (foundChain) { - return foundChain; - } else { - throw new Error(`Can't get network config for chain ${chainId}`); - } -}; - -export function NetworkConfigProvider({ children }: { children: ReactNode }) { - const chainId = useChainId(); - const [currentConfig, setCurrentConfig] = useState(getNetworkConfig(chainId)); +// Custom hook to use the network config +export const useSetNetworkConfig = () => { + const { getConfigByChainId, setCurrentConfig } = useNetworkConfigStore(); + const currentChainId = useChainId(); + // Update currentConfig when chainId changes useEffect(() => { - setCurrentConfig(getNetworkConfig(chainId)); - }, [chainId]); - - const contextValue: NetworkConfigContextType = { - currentConfig, - getConfigByChainId: getNetworkConfig, - }; - - return ( - {children} - ); -} + try { + const newConfig = getConfigByChainId(currentChainId); + setCurrentConfig(newConfig); + } catch (error) { + console.error(error); + } + }, [currentChainId, getConfigByChainId, setCurrentConfig]); +}; diff --git a/src/providers/NetworkConfig/useNetworkConfigStore.ts b/src/providers/NetworkConfig/useNetworkConfigStore.ts new file mode 100644 index 0000000000..ad2950db82 --- /dev/null +++ b/src/providers/NetworkConfig/useNetworkConfigStore.ts @@ -0,0 +1,31 @@ +import { create } from 'zustand'; +import { NetworkConfig } from '../../types/network'; +import { networks } from './networks'; +import { sepoliaConfig } from './networks/sepolia'; + +interface NetworkConfigStore extends NetworkConfig { + getConfigByChainId: (chainId?: number) => NetworkConfig; + setCurrentConfig: (config: NetworkConfig) => void; +} + +export const supportedNetworks = Object.values(networks).sort((a, b) => a.order - b.order); + +export const moralisSupportedChainIds = supportedNetworks + .filter(network => network.moralis.chainSupported) + .map(network => network.chain.id); + +export const getNetworkConfig = (chainId?: number): NetworkConfig => { + const foundChain = supportedNetworks.find(network => network.chain.id === chainId); + if (foundChain) { + return foundChain; + } else { + throw new Error(`Can't get network config for chain ${chainId}`); + } +}; + +// Create the Zustand store +export const useNetworkConfigStore = create(set => ({ + ...sepoliaConfig, + getConfigByChainId: getNetworkConfig, + setCurrentConfig: (config: NetworkConfig) => set({ ...config }), +})); diff --git a/src/providers/Providers.tsx b/src/providers/Providers.tsx index ff12e02c54..dfb14b094d 100644 --- a/src/providers/Providers.tsx +++ b/src/providers/Providers.tsx @@ -9,7 +9,6 @@ import { ErrorBoundary } from '../components/ui/utils/ErrorBoundary'; import { TopErrorFallback } from '../components/ui/utils/TopErrorFallback'; import graphQLClient from '../graphql'; import { AppProvider } from './App/AppProvider'; -import { NetworkConfigProvider } from './NetworkConfig/NetworkConfigProvider'; import { queryClient, wagmiConfig } from './NetworkConfig/web3-modal.config'; export default function Providers({ children }: { children: ReactNode }) { @@ -25,19 +24,17 @@ export default function Providers({ children }: { children: ReactNode }) { - - - - {children} - - + + + {children} + From 44e090bfebc1148e5ccab181cdcf5b6486e449ea Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 5 Dec 2024 03:01:57 -0500 Subject: [PATCH 02/48] Refactor network configuration imports to use new Zustand store --- netlify/shared/moralisBalances.mts | 2 +- src/components/DAOTreasury/components/Transactions.tsx | 4 ++-- .../DAOTreasury/hooks/useTreasuryLidoInteractions.ts | 4 ++-- src/components/DaoCreator/StepController.tsx | 4 ++-- src/components/DaoCreator/StepWrapper.tsx | 4 ++-- .../DaoCreator/formComponents/EstablishEssentials.tsx | 4 ++-- src/components/DaoDashboard/Activities/ProposalsHome.tsx | 4 ++-- src/components/DaoDashboard/Info/ParentLink.tsx | 4 ++-- src/components/DaoHierarchy/DaoHierarchyNode.tsx | 4 ++-- src/components/ProposalBuilder/index.tsx | 4 ++-- src/components/ProposalTemplates/ProposalTemplateCard.tsx | 4 ++-- .../Proposals/MultisigProposalDetails/TxActions.tsx | 4 ++-- src/components/Proposals/ProposalCard/ProposalCard.tsx | 4 ++-- src/components/Roles/RolePaymentDetails.tsx | 4 ++-- src/components/Roles/RoleTerm.tsx | 4 ++-- src/components/Roles/forms/RoleFormCreateProposal.tsx | 4 ++-- src/components/Roles/forms/RoleFormTabs.tsx | 4 ++-- src/components/SafeSettings/SettingsNavigation.tsx | 4 ++-- .../SafeSettings/Signers/modals/RemoveSignerModal.tsx | 4 ++-- src/components/ui/cards/DAOInfoCard.tsx | 4 ++-- src/components/ui/forms/ABISelector.tsx | 4 ++-- src/components/ui/links/EtherscanLink.tsx | 4 ++-- .../ui/menus/AccountDisplay/NetworkSelector.tsx | 6 +++--- src/components/ui/menus/CreateProposalMenu/index.tsx | 4 ++-- src/components/ui/menus/DAOSearch/SearchDisplay.tsx | 4 ++-- src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx | 4 ++-- src/components/ui/menus/SafesMenu/SafeMenuItem.tsx | 4 ++-- src/components/ui/modals/AddStrategyPermissionModal.tsx | 4 ++-- src/components/ui/modals/ConfirmDeleteStrategyModal.tsx | 4 ++-- src/components/ui/modals/ConfirmModifyGovernanceModal.tsx | 4 ++-- src/components/ui/modals/ForkProposalTemplateModal.tsx | 4 ++-- src/components/ui/modals/PaymentWithdrawModal.tsx | 4 ++-- src/components/ui/modals/ProposalTemplateModal.tsx | 4 ++-- src/components/ui/modals/Stake.tsx | 4 ++-- src/components/ui/page/Global/index.tsx | 2 +- src/components/ui/page/Header/PageHeader.tsx | 4 ++-- src/components/ui/page/Navigation/NavigationLinks.tsx | 4 ++-- src/hooks/DAO/loaders/useDecentTreasury.ts | 4 ++-- src/hooks/DAO/loaders/useFavorites.ts | 4 ++-- src/hooks/DAO/loaders/useFractalGovernance.ts | 4 ++-- src/hooks/DAO/loaders/useFractalGuardContracts.ts | 4 ++-- src/hooks/DAO/loaders/useFractalNode.ts | 4 ++-- src/hooks/DAO/loaders/useHatsTree.ts | 4 ++-- src/hooks/DAO/proposal/useCreateProposalTemplate.ts | 4 ++-- src/hooks/DAO/proposal/useRemoveProposalTemplate.ts | 4 ++-- src/hooks/DAO/proposal/useSubmitProposal.ts | 4 ++-- src/hooks/DAO/useBuildDAOTx.ts | 4 ++-- src/hooks/DAO/useCreateSubDAOProposal.ts | 4 ++-- src/hooks/DAO/useDeployAzorius.ts | 4 ++-- src/hooks/DAO/useDeployDAO.ts | 4 ++-- src/hooks/DAO/useKeyValuePairs.ts | 4 ++-- src/hooks/DAO/useParseSafeAddress.ts | 4 ++-- src/hooks/DAO/useSearchDao.ts | 4 ++-- src/hooks/schemas/DAOCreate/useDAOCreateTests.ts | 4 ++-- src/hooks/schemas/common/useValidationAddress.tsx | 4 ++-- src/hooks/stake/lido/useLidoStaking.ts | 4 ++-- src/hooks/streams/useCreateSablierStream.ts | 4 ++-- src/hooks/utils/cache/useLocalDB.ts | 4 ++-- src/hooks/utils/useAutomaticSwitchChain.ts | 4 ++-- src/hooks/utils/useAvatar.ts | 4 ++-- src/hooks/utils/useCreateRoles.ts | 4 ++-- src/hooks/utils/useGetSafeName.ts | 8 ++++---- src/hooks/utils/useSafeDecoder.tsx | 4 ++-- src/main.tsx | 7 +++++-- src/pages/LoadingProblem.tsx | 4 ++-- src/pages/dao/edit/governance/SafeEditGovernancePage.tsx | 4 ++-- src/pages/dao/hierarchy/SafeHierarchyPage.tsx | 4 ++-- .../dao/proposal-templates/SafeProposalTemplatesPage.tsx | 4 ++-- src/pages/dao/proposals/[proposalId]/index.tsx | 4 ++-- .../new/sablier/SafeSablierProposalCreatePage.tsx | 4 ++-- src/pages/dao/roles/SafeRolesPage.tsx | 4 ++-- src/pages/dao/roles/details/SafeRoleDetailsPage.tsx | 4 ++-- src/pages/dao/roles/edit/SafeRolesEditPage.tsx | 4 ++-- .../dao/roles/edit/details/SafeRoleEditDetailsPage.tsx | 4 ++-- .../edit/summary/SafeRolesEditProposalSummaryPage.tsx | 4 ++-- .../dao/settings/general/SafeGeneralSettingsPage.tsx | 4 ++-- .../settings/governance/SafeGovernanceSettingsPage.tsx | 4 ++-- .../modules-and-guard/SafeModulesSettingsPage.tsx | 4 ++-- .../permissions/SafePermissionsCreateProposal.tsx | 4 ++-- .../settings/permissions/SafePermissionsSettingsPage.tsx | 4 ++-- src/pages/home/SafeDisplayRow.tsx | 4 ++-- src/providers/App/hooks/useBalancesAPI.ts | 4 ++-- src/providers/App/hooks/useSafeAPI.ts | 4 ++-- src/providers/NetworkConfig/web3-modal.config.ts | 2 +- src/utils/url.ts | 2 +- 85 files changed, 172 insertions(+), 169 deletions(-) diff --git a/netlify/shared/moralisBalances.mts b/netlify/shared/moralisBalances.mts index 43708b60f8..74b4131bd1 100644 --- a/netlify/shared/moralisBalances.mts +++ b/netlify/shared/moralisBalances.mts @@ -3,7 +3,7 @@ import type { Store } from '@netlify/blobs'; import Moralis from 'moralis'; import { isAddress } from 'viem'; import type { Address } from 'viem'; -import { moralisSupportedChainIds } from '../../src/providers/NetworkConfig/NetworkConfigProvider'; +import { moralisSupportedChainIds } from '../../src/providers/NetworkConfig/useNetworkConfigStore'; export interface BalanceDataWithMetadata { data: T[]; diff --git a/src/components/DAOTreasury/components/Transactions.tsx b/src/components/DAOTreasury/components/Transactions.tsx index 5d70c4c210..59c6ef1249 100644 --- a/src/components/DAOTreasury/components/Transactions.tsx +++ b/src/components/DAOTreasury/components/Transactions.tsx @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'; import { getAddress } from 'viem'; import { useDateTimeDisplay } from '../../../helpers/dateTime'; import { useFractal } from '../../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { TokenEventType, TransferDisplayData, TransferType } from '../../../types'; import { DecentTooltip } from '../../ui/DecentTooltip'; @@ -14,7 +14,7 @@ import { BarLoader } from '../../ui/loaders/BarLoader'; function TransferRow({ displayData }: { displayData: TransferDisplayData }) { const { t } = useTranslation(['treasury', 'common']); - const { etherscanBaseURL } = useNetworkConfig(); + const { etherscanBaseURL } = useNetworkConfigStore(); return ( !asset.tokenAddress); const { handleUnstake, handleClaimUnstakedETH } = useLidoStaking(); const { canUserCreateProposal } = useCanUserCreateProposal(); - const { staking } = useNetworkConfig(); + const { staking } = useNetworkConfigStore(); const publicClient = usePublicClient(); // --- Lido Stake button setup --- diff --git a/src/components/DaoCreator/StepController.tsx b/src/components/DaoCreator/StepController.tsx index 3c13b9306a..2d1e83fcd6 100644 --- a/src/components/DaoCreator/StepController.tsx +++ b/src/components/DaoCreator/StepController.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'; import { Navigate, Route, Routes, useLocation } from 'react-router-dom'; import { toast } from 'sonner'; import { useAccount } from 'wagmi'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { ChildERC20Steps, ChildERC721Steps, @@ -25,7 +25,7 @@ import useStepRedirect from './hooks/useStepRedirect'; function StepController(props: Omit) { const { t } = useTranslation('daoCreate'); - const { createOptions } = useNetworkConfig(); + const { createOptions } = useNetworkConfigStore(); const location = useLocation(); const { values, mode, setFieldValue } = props; diff --git a/src/components/DaoCreator/StepWrapper.tsx b/src/components/DaoCreator/StepWrapper.tsx index 25f72378ca..d35ab2169b 100644 --- a/src/components/DaoCreator/StepWrapper.tsx +++ b/src/components/DaoCreator/StepWrapper.tsx @@ -4,7 +4,7 @@ import { ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { BASE_ROUTES, DAO_ROUTES } from '../../constants/routes'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore'; import PageHeader from '../ui/page/Header/PageHeader'; import { DAOCreateMode } from './formComponents/EstablishEssentials'; @@ -27,7 +27,7 @@ export function StepWrapper({ shouldWrapChildren = true, }: IStepWrapper) { const { safe } = useDaoInfoStore(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { t } = useTranslation(['daoCreate']); const navigate = useNavigate(); diff --git a/src/components/DaoCreator/formComponents/EstablishEssentials.tsx b/src/components/DaoCreator/formComponents/EstablishEssentials.tsx index 9f3ab5fdea..26d1aea7b2 100644 --- a/src/components/DaoCreator/formComponents/EstablishEssentials.tsx +++ b/src/components/DaoCreator/formComponents/EstablishEssentials.tsx @@ -3,7 +3,7 @@ import debounce from 'lodash.debounce'; import { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { createAccountSubstring } from '../../../hooks/utils/useGetAccountName'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { GovernanceType, ICreationStepProps, VotingStrategyType } from '../../../types'; import { InputComponent, LabelComponent } from '../../ui/forms/InputComponent'; @@ -65,7 +65,7 @@ export function EstablishEssentials(props: ICreationStepProps) { setFieldValue('essentials.governance', value); }; - const { createOptions } = useNetworkConfig(); + const { createOptions } = useNetworkConfigStore(); const [snapshotENSInput, setSnapshotENSInput] = useState(''); diff --git a/src/components/DaoDashboard/Activities/ProposalsHome.tsx b/src/components/DaoDashboard/Activities/ProposalsHome.tsx index c4d8b8f211..15093a701b 100644 --- a/src/components/DaoDashboard/Activities/ProposalsHome.tsx +++ b/src/components/DaoDashboard/Activities/ProposalsHome.tsx @@ -7,7 +7,7 @@ import { DAO_ROUTES } from '../../../constants/routes'; import { useProposalsSortedAndFiltered } from '../../../hooks/DAO/proposal/useProposals'; import { useCanUserCreateProposal } from '../../../hooks/utils/useCanUserSubmitProposal'; import { useFractal } from '../../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { AzoriusGovernance, @@ -39,7 +39,7 @@ export function ProposalsHome() { const { governance, guardContracts } = useFractal(); const { safe } = useDaoInfoStore(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const azoriusGovernance = governance as AzoriusGovernance; const delegate = useDecentModal(ModalType.DELEGATE); diff --git a/src/components/DaoDashboard/Info/ParentLink.tsx b/src/components/DaoDashboard/Info/ParentLink.tsx index 0adf1499dd..940e2aa058 100644 --- a/src/components/DaoDashboard/Info/ParentLink.tsx +++ b/src/components/DaoDashboard/Info/ParentLink.tsx @@ -3,14 +3,14 @@ import { ArrowBendLeftUp } from '@phosphor-icons/react'; import { useTranslation } from 'react-i18next'; import { Link as RouterLink } from 'react-router-dom'; import { DAO_ROUTES } from '../../../constants/routes'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; /** * Displays a link to the current DAO's parent, if it has one. */ export function ParentLink() { const { subgraphInfo } = useDaoInfoStore(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { t } = useTranslation('breadcrumbs'); if (!subgraphInfo?.parentAddress) { diff --git a/src/components/DaoHierarchy/DaoHierarchyNode.tsx b/src/components/DaoHierarchy/DaoHierarchyNode.tsx index f51e7fbb2a..3d4e079bab 100644 --- a/src/components/DaoHierarchy/DaoHierarchyNode.tsx +++ b/src/components/DaoHierarchy/DaoHierarchyNode.tsx @@ -15,7 +15,7 @@ import { CacheKeys } from '../../hooks/utils/cache/cacheDefaults'; import { setValue, getValue } from '../../hooks/utils/cache/useLocalStorage'; import { useAddressContractType } from '../../hooks/utils/useAddressContractType'; import { useSafeAPI } from '../../providers/App/hooks/useSafeAPI'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore'; import { DaoHierarchyInfo, DaoHierarchyStrategyType, DecentModule } from '../../types'; import { getAzoriusModuleFromModules } from '../../utils'; @@ -43,7 +43,7 @@ export function DaoHierarchyNode({ const safeApi = useSafeAPI(); const [hierarchyNode, setHierarchyNode] = useState(); const [hasErrorLoading, setErrorLoading] = useState(false); - const { addressPrefix, subgraph } = useNetworkConfig(); + const { addressPrefix, subgraph } = useNetworkConfigStore(); const chainId = useChainId(); const publicClient = usePublicClient(); diff --git a/src/components/ProposalBuilder/index.tsx b/src/components/ProposalBuilder/index.tsx index 206405d772..b06328ecf0 100644 --- a/src/components/ProposalBuilder/index.tsx +++ b/src/components/ProposalBuilder/index.tsx @@ -11,7 +11,7 @@ import useSubmitProposal from '../../hooks/DAO/proposal/useSubmitProposal'; import useCreateProposalSchema from '../../hooks/schemas/proposalBuilder/useCreateProposalSchema'; import { useCanUserCreateProposal } from '../../hooks/utils/useCanUserSubmitProposal'; import { useFractal } from '../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { useProposalActionsStore } from '../../store/actions/useProposalActionsStore'; import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore'; import { CreateProposalSteps, ProposalExecuteData } from '../../types'; @@ -58,7 +58,7 @@ export function ProposalBuilder({ const { safe } = useDaoInfoStore(); const safeAddress = safe?.address; - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { submitProposal, pendingCreateTx } = useSubmitProposal(); const { canUserCreateProposal } = useCanUserCreateProposal(); const { createProposalValidation } = useCreateProposalSchema(); diff --git a/src/components/ProposalTemplates/ProposalTemplateCard.tsx b/src/components/ProposalTemplates/ProposalTemplateCard.tsx index 9d6d4e19b5..b96855fa60 100644 --- a/src/components/ProposalTemplates/ProposalTemplateCard.tsx +++ b/src/components/ProposalTemplates/ProposalTemplateCard.tsx @@ -7,7 +7,7 @@ import { DAO_ROUTES } from '../../constants/routes'; import useRemoveProposalTemplate from '../../hooks/DAO/proposal/useRemoveProposalTemplate'; import useSubmitProposal from '../../hooks/DAO/proposal/useSubmitProposal'; import { useCanUserCreateProposal } from '../../hooks/utils/useCanUserSubmitProposal'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore'; import { ProposalTemplate } from '../../types/proposalBuilder'; import ContentBox from '../ui/containers/ContentBox'; @@ -29,7 +29,7 @@ export default function ProposalTemplateCard({ const { t } = useTranslation('proposalTemplate'); const { safe } = useDaoInfoStore(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { prepareRemoveProposalTemplateProposal } = useRemoveProposalTemplate(); const { submitProposal } = useSubmitProposal(); diff --git a/src/components/Proposals/MultisigProposalDetails/TxActions.tsx b/src/components/Proposals/MultisigProposalDetails/TxActions.tsx index 216c3146d6..a08e560a44 100644 --- a/src/components/Proposals/MultisigProposalDetails/TxActions.tsx +++ b/src/components/Proposals/MultisigProposalDetails/TxActions.tsx @@ -14,7 +14,7 @@ import { useAsyncRequest } from '../../../hooks/utils/useAsyncRequest'; import { useTransaction } from '../../../hooks/utils/useTransaction'; import { useFractal } from '../../../providers/App/AppProvider'; import { useSafeAPI } from '../../../providers/App/hooks/useSafeAPI'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { MultisigProposal, FractalProposalState } from '../../../types'; import { DecentTooltip } from '../../ui/DecentTooltip'; @@ -42,7 +42,7 @@ export function TxActions({ proposal }: { proposal: MultisigProposal }) { } }, [proposal.state]); - const { chain } = useNetworkConfig(); + const { chain } = useNetworkConfigStore(); const { t } = useTranslation(['proposal', 'common', 'transaction']); const [asyncRequest, asyncRequestPending] = useAsyncRequest(); diff --git a/src/components/Proposals/ProposalCard/ProposalCard.tsx b/src/components/Proposals/ProposalCard/ProposalCard.tsx index 55cbf89f83..532c31da8f 100644 --- a/src/components/Proposals/ProposalCard/ProposalCard.tsx +++ b/src/components/Proposals/ProposalCard/ProposalCard.tsx @@ -3,7 +3,7 @@ import { format } from 'date-fns'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import { DAO_ROUTES } from '../../../constants/routes'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { AzoriusProposal, FractalProposal, SnapshotProposal } from '../../../types'; import { DEFAULT_DATE_FORMAT } from '../../../utils'; @@ -15,7 +15,7 @@ import { ProposalCountdown } from '../../ui/proposal/ProposalCountdown'; function ProposalCard({ proposal }: { proposal: FractalProposal }) { const { safe } = useDaoInfoStore(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { t } = useTranslation('common'); if (!safe?.address) { diff --git a/src/components/Roles/RolePaymentDetails.tsx b/src/components/Roles/RolePaymentDetails.tsx index 27af8401a7..9cb3fde6b0 100644 --- a/src/components/Roles/RolePaymentDetails.tsx +++ b/src/components/Roles/RolePaymentDetails.tsx @@ -9,7 +9,7 @@ import { useAccount, usePublicClient } from 'wagmi'; import { DETAILS_BOX_SHADOW, isDemoMode } from '../../constants/common'; import { DAO_ROUTES } from '../../constants/routes'; import { useFractal } from '../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore'; import { useRolesStore } from '../../store/roles/useRolesStore'; import { BigIntValuePair } from '../../types'; @@ -145,7 +145,7 @@ export function RolePaymentDetails({ } = useFractal(); const { safe } = useDaoInfoStore(); const { address: connectedAccount } = useAccount(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { refreshWithdrawableAmount } = useRolesStore(); const navigate = useNavigate(); const publicClient = usePublicClient(); diff --git a/src/components/Roles/RoleTerm.tsx b/src/components/Roles/RoleTerm.tsx index 5435953bbc..6b342afc6e 100644 --- a/src/components/Roles/RoleTerm.tsx +++ b/src/components/Roles/RoleTerm.tsx @@ -12,7 +12,7 @@ import useAvatar from '../../hooks/utils/useAvatar'; import { useCopyText } from '../../hooks/utils/useCopyText'; import { useGetAccountName } from '../../hooks/utils/useGetAccountName'; import { useTransaction } from '../../hooks/utils/useTransaction'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { useRolesStore } from '../../store/roles/useRolesStore'; import { RoleFormTermStatus } from '../../types/roles'; import { DEFAULT_DATE_TIME_FORMAT_NO_TZ } from '../../utils'; @@ -284,7 +284,7 @@ export default function RoleTerm({ const { t } = useTranslation(['roles']); const { contracts: { hatsProtocol }, - } = useNetworkConfig(); + } = useNetworkConfigStore(); const roleHat = useMemo(() => { if (!hatId) return undefined; diff --git a/src/components/Roles/forms/RoleFormCreateProposal.tsx b/src/components/Roles/forms/RoleFormCreateProposal.tsx index 890275e68a..c9af4019fc 100644 --- a/src/components/Roles/forms/RoleFormCreateProposal.tsx +++ b/src/components/Roles/forms/RoleFormCreateProposal.tsx @@ -7,7 +7,7 @@ import { useNavigate } from 'react-router-dom'; import { getAddress, Hex, zeroAddress } from 'viem'; import { CARD_SHADOW } from '../../../constants/common'; import { DAO_ROUTES } from '../../../constants/routes'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { EditedRole, @@ -114,7 +114,7 @@ export default function RoleFormCreateProposal({ close }: { close: () => void }) const { safe } = useDaoInfoStore(); const navigate = useNavigate(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const safeAddress = safe?.address; diff --git a/src/components/Roles/forms/RoleFormTabs.tsx b/src/components/Roles/forms/RoleFormTabs.tsx index b1ff639d5c..48684a4136 100644 --- a/src/components/Roles/forms/RoleFormTabs.tsx +++ b/src/components/Roles/forms/RoleFormTabs.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'; import { Blocker, useNavigate } from 'react-router-dom'; import { Hex } from 'viem'; import { DAO_ROUTES } from '../../../constants/routes'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { useRolesStore } from '../../../store/roles/useRolesStore'; import { EditBadgeStatus, RoleFormValues, RoleHatFormValue } from '../../../types/roles'; @@ -28,7 +28,7 @@ export function RoleFormTabs({ const { hatsTree } = useRolesStore(); const { safe } = useDaoInfoStore(); const navigate = useNavigate(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { editedRoleData, isRoleUpdated, existingRoleHat } = useRoleFormEditedRole({ hatsTree }); const { t } = useTranslation(['roles']); const { values, setFieldValue, errors, setTouched } = useFormikContext(); diff --git a/src/components/SafeSettings/SettingsNavigation.tsx b/src/components/SafeSettings/SettingsNavigation.tsx index 7826c61b84..6fddcbfa1b 100644 --- a/src/components/SafeSettings/SettingsNavigation.tsx +++ b/src/components/SafeSettings/SettingsNavigation.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'; import { Link, useLocation, useMatch } from 'react-router-dom'; import { DAO_ROUTES } from '../../constants/routes'; import { useFractal } from '../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore'; import { AzoriusGovernance } from '../../types'; import { BarLoader } from '../ui/loaders/BarLoader'; @@ -81,7 +81,7 @@ function SettingsLink({ export default function SettingsNavigation() { const { t } = useTranslation('settings'); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { governance } = useFractal(); const { safe, modules } = useDaoInfoStore(); const azoriusGovernance = governance as AzoriusGovernance; diff --git a/src/components/SafeSettings/Signers/modals/RemoveSignerModal.tsx b/src/components/SafeSettings/Signers/modals/RemoveSignerModal.tsx index a3ee814f92..85e7eddd32 100644 --- a/src/components/SafeSettings/Signers/modals/RemoveSignerModal.tsx +++ b/src/components/SafeSettings/Signers/modals/RemoveSignerModal.tsx @@ -4,7 +4,7 @@ import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Address } from 'viem'; import { useEnsName } from 'wagmi'; -import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import { SENTINEL_MODULE } from '../../../../utils/address'; import SupportTooltip from '../../../ui/badges/SupportTooltip'; @@ -32,7 +32,7 @@ function RemoveSignerModal({ const [threshold, setThreshold] = useState(defaultNewThreshold); const [nonce, setNonce] = useState(safe!.nextNonce); - const { chain } = useNetworkConfig(); + const { chain } = useNetworkConfigStore(); const { data: ensName } = useEnsName({ address: selectedSigner, chainId: chain.id, diff --git a/src/components/ui/cards/DAOInfoCard.tsx b/src/components/ui/cards/DAOInfoCard.tsx index be13e79780..7b7a84379a 100644 --- a/src/components/ui/cards/DAOInfoCard.tsx +++ b/src/components/ui/cards/DAOInfoCard.tsx @@ -3,7 +3,7 @@ import { Link as RouterLink } from 'react-router-dom'; import { DAO_ROUTES } from '../../../constants/routes'; import { useAccountFavorites } from '../../../hooks/DAO/loaders/useFavorites'; import { useGetAccountName } from '../../../hooks/utils/useGetAccountName'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { SnapshotButton } from '../badges/Snapshot'; import { FavoriteIcon } from '../icons/FavoriteIcon'; @@ -16,7 +16,7 @@ import { ManageDAOMenu } from '../menus/ManageDAO/ManageDAOMenu'; */ export function DAOInfoCard() { const node = useDaoInfoStore(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); // for non Fractal Safes const displayedAddress = node.safe?.address; const { displayName } = useGetAccountName(displayedAddress); diff --git a/src/components/ui/forms/ABISelector.tsx b/src/components/ui/forms/ABISelector.tsx index 886df3b685..bfbd9e64f6 100644 --- a/src/components/ui/forms/ABISelector.tsx +++ b/src/components/ui/forms/ABISelector.tsx @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'; import { isAddress } from 'viem'; import { useEnsAddress, usePublicClient } from 'wagmi'; import { logError } from '../../../helpers/errorLogging'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { LabelComponent } from './InputComponent'; export type ABIElement = { @@ -26,7 +26,7 @@ interface IABISelector { export default function ABISelector({ target, onChange }: IABISelector) { const [abi, setABI] = useState([]); - const { etherscanAPIUrl } = useNetworkConfig(); + const { etherscanAPIUrl } = useNetworkConfigStore(); const { t } = useTranslation('common'); const { data: ensAddress } = useEnsAddress({ name: target?.toLowerCase() }); const client = usePublicClient(); diff --git a/src/components/ui/links/EtherscanLink.tsx b/src/components/ui/links/EtherscanLink.tsx index 02a8c577f3..a58b245ad6 100644 --- a/src/components/ui/links/EtherscanLink.tsx +++ b/src/components/ui/links/EtherscanLink.tsx @@ -1,7 +1,7 @@ import { Box, LinkProps } from '@chakra-ui/react'; import { useRef } from 'react'; import { useTranslation } from 'react-i18next'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import ModalTooltip from '../modals/ModalTooltip'; import ExternalLink from './ExternalLink'; @@ -20,7 +20,7 @@ export default function EtherscanLink({ secondaryValue, ...rest }: EtherscanLinkProps) { - const { etherscanBaseURL } = useNetworkConfig(); + const { etherscanBaseURL } = useNetworkConfigStore(); const { t } = useTranslation(); const containerRef = useRef(null); diff --git a/src/components/ui/menus/AccountDisplay/NetworkSelector.tsx b/src/components/ui/menus/AccountDisplay/NetworkSelector.tsx index 567d4061d2..3d9cae3a08 100644 --- a/src/components/ui/menus/AccountDisplay/NetworkSelector.tsx +++ b/src/components/ui/menus/AccountDisplay/NetworkSelector.tsx @@ -5,8 +5,8 @@ import { useTranslation } from 'react-i18next'; import { useSwitchChain } from 'wagmi'; import { supportedNetworks, - useNetworkConfig, -} from '../../../../providers/NetworkConfig/NetworkConfigProvider'; + useNetworkConfigStore, +} from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { OptionMenu } from '../OptionMenu'; /** @@ -18,7 +18,7 @@ export function NetworkSelector({ containerRef: RefObject; }) { const { t } = useTranslation('menu'); - const { chain } = useNetworkConfig(); + const { chain } = useNetworkConfigStore(); const { switchChain } = useSwitchChain(); const networksOptions = supportedNetworks.map(network => ({ optionKey: network.chain.name, diff --git a/src/components/ui/menus/CreateProposalMenu/index.tsx b/src/components/ui/menus/CreateProposalMenu/index.tsx index b072a51e86..d13f7b2dc5 100644 --- a/src/components/ui/menus/CreateProposalMenu/index.tsx +++ b/src/components/ui/menus/CreateProposalMenu/index.tsx @@ -17,13 +17,13 @@ import { useNavigate } from 'react-router-dom'; import { Address } from 'viem'; import { NEUTRAL_2_82_TRANSPARENT } from '../../../../constants/common'; import { DAO_ROUTES } from '../../../../constants/routes'; -import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { EaseOutComponent } from '../../utils/EaseOutComponent'; export function CreateProposalMenu({ safeAddress }: { safeAddress: Address }) { const { t } = useTranslation('proposal'); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const navigate = useNavigate(); diff --git a/src/components/ui/menus/DAOSearch/SearchDisplay.tsx b/src/components/ui/menus/DAOSearch/SearchDisplay.tsx index b696e7a3be..aa1a7ac85f 100644 --- a/src/components/ui/menus/DAOSearch/SearchDisplay.tsx +++ b/src/components/ui/menus/DAOSearch/SearchDisplay.tsx @@ -4,7 +4,7 @@ import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Address } from 'viem'; import { SafeDisplayRow } from '../../../../pages/home/SafeDisplayRow'; -import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import { ErrorBoundary } from '../../utils/ErrorBoundary'; import { MySafesErrorFallback } from '../../utils/MySafesErrorFallback'; @@ -19,7 +19,7 @@ interface ISearchDisplay { export function SearchDisplay({ loading, errorMessage, address, onClickView }: ISearchDisplay) { const { t } = useTranslation(['common', 'dashboard']); const node = useDaoInfoStore(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const isCurrentSafe = useMemo( () => !!node && !!node?.safe?.address && node.safe.address === address, diff --git a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx index 77d59791f0..440222d94f 100644 --- a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx +++ b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx @@ -15,7 +15,7 @@ import useClawBack from '../../../../hooks/DAO/useClawBack'; import useBlockTimestamp from '../../../../hooks/utils/useBlockTimestamp'; import { useCanUserCreateProposal } from '../../../../hooks/utils/useCanUserSubmitProposal'; import { useFractal } from '../../../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import { FractalModuleType, FreezeVotingType, GovernanceType } from '../../../../types'; import { ModalType } from '../../modals/ModalProvider'; @@ -42,7 +42,7 @@ export function ManageDAOMenu() { }, }); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const handleNavigateToSettings = useCallback(() => { if (safeAddress) { diff --git a/src/components/ui/menus/SafesMenu/SafeMenuItem.tsx b/src/components/ui/menus/SafesMenu/SafeMenuItem.tsx index 0966c8a80a..76d5008051 100644 --- a/src/components/ui/menus/SafesMenu/SafeMenuItem.tsx +++ b/src/components/ui/menus/SafesMenu/SafeMenuItem.tsx @@ -5,7 +5,7 @@ import { Address } from 'viem'; import { useSwitchChain } from 'wagmi'; import { DAO_ROUTES } from '../../../../constants/routes'; import useAvatar from '../../../../hooks/utils/useAvatar'; -import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { getChainIdFromPrefix, getNetworkIcon } from '../../../../utils/url'; import Avatar from '../../page/Header/Avatar'; @@ -20,7 +20,7 @@ export interface SafeMenuItemProps { export function SafeMenuItem({ address, network, name }: SafeMenuItemProps) { const navigate = useNavigate(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { switchChain } = useSwitchChain({ mutation: { onSuccess: () => { diff --git a/src/components/ui/modals/AddStrategyPermissionModal.tsx b/src/components/ui/modals/AddStrategyPermissionModal.tsx index 281239bd71..9ca1380559 100644 --- a/src/components/ui/modals/AddStrategyPermissionModal.tsx +++ b/src/components/ui/modals/AddStrategyPermissionModal.tsx @@ -4,14 +4,14 @@ import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { zeroAddress } from 'viem'; import { DAO_ROUTES } from '../../../constants/routes'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { Card } from '../cards/Card'; export default function AddStrategyPermissionModal({ closeModal }: { closeModal: () => void }) { const { t } = useTranslation(['settings', 'common']); const navigate = useNavigate(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { safe } = useDaoInfoStore(); if (!safe) { diff --git a/src/components/ui/modals/ConfirmDeleteStrategyModal.tsx b/src/components/ui/modals/ConfirmDeleteStrategyModal.tsx index 2d30ed4726..d5434c3973 100644 --- a/src/components/ui/modals/ConfirmDeleteStrategyModal.tsx +++ b/src/components/ui/modals/ConfirmDeleteStrategyModal.tsx @@ -6,7 +6,7 @@ import { toast } from 'sonner'; import { DAO_ROUTES } from '../../../constants/routes'; import useVotingStrategiesAddresses from '../../../hooks/utils/useVotingStrategiesAddresses'; import { useFractal } from '../../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useProposalActionsStore } from '../../../store/actions/useProposalActionsStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { AzoriusGovernance, CreateProposalTransaction, ProposalActionType } from '../../../types'; @@ -16,7 +16,7 @@ import { SafePermissionsStrategyAction } from '../../SafeSettings/SafePermission export function ConfirmDeleteStrategyModal({ onClose }: { onClose: () => void }) { const navigate = useNavigate(); const { t } = useTranslation('settings'); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { governance, governanceContracts } = useFractal(); const { safe } = useDaoInfoStore(); const { addAction } = useProposalActionsStore(); diff --git a/src/components/ui/modals/ConfirmModifyGovernanceModal.tsx b/src/components/ui/modals/ConfirmModifyGovernanceModal.tsx index b5a8acffb3..70346a01f8 100644 --- a/src/components/ui/modals/ConfirmModifyGovernanceModal.tsx +++ b/src/components/ui/modals/ConfirmModifyGovernanceModal.tsx @@ -2,14 +2,14 @@ import { Box, Button, Text } from '@chakra-ui/react'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import { DAO_ROUTES } from '../../../constants/routes'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import Divider from '../utils/Divider'; export function ConfirmModifyGovernanceModal({ close }: { close: () => void }) { const { t } = useTranslation('modals'); const { safe } = useDaoInfoStore(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); if (!safe?.address) { return null; diff --git a/src/components/ui/modals/ForkProposalTemplateModal.tsx b/src/components/ui/modals/ForkProposalTemplateModal.tsx index 2c6d07c8ff..4f344f6149 100644 --- a/src/components/ui/modals/ForkProposalTemplateModal.tsx +++ b/src/components/ui/modals/ForkProposalTemplateModal.tsx @@ -8,7 +8,7 @@ import { DAO_ROUTES } from '../../../constants/routes'; import { useIsSafe } from '../../../hooks/safe/useIsSafe'; import { validateAddress } from '../../../hooks/schemas/common/useValidationAddress'; import { useCanUserCreateProposal } from '../../../hooks/utils/useCanUserSubmitProposal'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { ProposalTemplate } from '../../../types/proposalBuilder'; import { InputComponent } from '../forms/InputComponent'; @@ -32,7 +32,7 @@ export default function ForkProposalTemplateModal({ const { t } = useTranslation('proposalTemplate'); const navigate = useNavigate(); const publicClient = usePublicClient(); - const { chain, addressPrefix } = useNetworkConfig(); + const { chain, addressPrefix } = useNetworkConfigStore(); const { subgraphInfo } = useDaoInfoStore(); const { isSafe, isSafeLoading } = useIsSafe(targetDAOAddress); diff --git a/src/components/ui/modals/PaymentWithdrawModal.tsx b/src/components/ui/modals/PaymentWithdrawModal.tsx index 32bb69e7b4..3cacfa8bb5 100644 --- a/src/components/ui/modals/PaymentWithdrawModal.tsx +++ b/src/components/ui/modals/PaymentWithdrawModal.tsx @@ -10,7 +10,7 @@ import { convertStreamIdToBigInt } from '../../../hooks/streams/useCreateSablier import useAvatar from '../../../hooks/utils/useAvatar'; import { useGetAccountName } from '../../../hooks/utils/useGetAccountName'; import { useTransaction } from '../../../hooks/utils/useTransaction'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { formatCoin } from '../../../utils'; import { getNetworkIcon } from '../../../utils/url'; import Avatar, { AvatarSize } from '../page/Header/Avatar'; @@ -44,7 +44,7 @@ export function PaymentWithdrawModal({ const { displayName: accountDisplayName } = useGetAccountName(withdrawInformation.recipient); const avatarURL = useAvatar(accountDisplayName); const iconSize = useBreakpointValue({ base: 'sm', md: 'icon' }) || 'sm'; - const { chain, addressPrefix } = useNetworkConfig(); + const { chain, addressPrefix } = useNetworkConfigStore(); const handleWithdraw = useCallback(async () => { if ( diff --git a/src/components/ui/modals/ProposalTemplateModal.tsx b/src/components/ui/modals/ProposalTemplateModal.tsx index 0491a72bcd..92663d5b92 100644 --- a/src/components/ui/modals/ProposalTemplateModal.tsx +++ b/src/components/ui/modals/ProposalTemplateModal.tsx @@ -7,7 +7,7 @@ import { logError } from '../../../helpers/errorLogging'; import { usePrepareProposal } from '../../../hooks/DAO/proposal/usePrepareProposal'; import useSubmitProposal from '../../../hooks/DAO/proposal/useSubmitProposal'; import { useCanUserCreateProposal } from '../../../hooks/utils/useCanUserSubmitProposal'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { BigIntValuePair } from '../../../types'; import { ProposalTemplate } from '../../../types/proposalBuilder'; @@ -26,7 +26,7 @@ export default function ProposalTemplateModal({ onClose, }: IProposalTemplateModalProps) { const { safe } = useDaoInfoStore(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const [filledProposalTransactions, setFilledProposalTransactions] = useState(transactions); const [nonce, setNonce] = useState(safe!.nextNonce); diff --git a/src/components/ui/modals/Stake.tsx b/src/components/ui/modals/Stake.tsx index 92d28b464a..12e1530105 100644 --- a/src/components/ui/modals/Stake.tsx +++ b/src/components/ui/modals/Stake.tsx @@ -5,7 +5,7 @@ import { useNavigate } from 'react-router-dom'; import { DAO_ROUTES } from '../../../constants/routes'; import useLidoStaking from '../../../hooks/stake/lido/useLidoStaking'; import { useFractal } from '../../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { BigIntValuePair, TokenBalance } from '../../../types'; import { BigIntInput } from '../forms/BigIntInput'; @@ -15,7 +15,7 @@ export default function StakeModal({ close }: { close: () => void }) { treasury: { assetsFungible }, } = useFractal(); const { safe } = useDaoInfoStore(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const navigate = useNavigate(); const { t } = useTranslation('stake'); diff --git a/src/components/ui/page/Global/index.tsx b/src/components/ui/page/Global/index.tsx index c95da3f37c..3a2e3df2ea 100644 --- a/src/components/ui/page/Global/index.tsx +++ b/src/components/ui/page/Global/index.tsx @@ -14,7 +14,7 @@ import { getSafeName } from '../../../../hooks/utils/useGetSafeName'; import { getNetworkConfig, supportedNetworks, -} from '../../../../providers/NetworkConfig/NetworkConfigProvider'; +} from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { wagmiConfig } from '../../../../providers/NetworkConfig/web3-modal.config'; import { getChainIdFromPrefix } from '../../../../utils/url'; import { Layout } from '../Layout'; diff --git a/src/components/ui/page/Header/PageHeader.tsx b/src/components/ui/page/Header/PageHeader.tsx index 1386663602..6dfc559ff7 100644 --- a/src/components/ui/page/Header/PageHeader.tsx +++ b/src/components/ui/page/Header/PageHeader.tsx @@ -4,7 +4,7 @@ import { ReactNode, useEffect, useState } from 'react'; import { CONTENT_MAXW } from '../../../../constants/common'; import { DAO_ROUTES } from '../../../../constants/routes'; import { createAccountSubstring } from '../../../../hooks/utils/useGetAccountName'; -import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import AddressCopier from '../../links/AddressCopier'; import Divider from '../../utils/Divider'; @@ -34,7 +34,7 @@ function PageHeader({ }: PageHeaderProps) { const { safe, subgraphInfo } = useDaoInfoStore(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const safeAddress = safe?.address; const [links, setLinks] = useState([...breadcrumbs]); diff --git a/src/components/ui/page/Navigation/NavigationLinks.tsx b/src/components/ui/page/Navigation/NavigationLinks.tsx index 62c67636d7..7855ef2536 100644 --- a/src/components/ui/page/Navigation/NavigationLinks.tsx +++ b/src/components/ui/page/Navigation/NavigationLinks.tsx @@ -2,7 +2,7 @@ import { Box, Flex, Hide } from '@chakra-ui/react'; import { BookOpen, Coins, GitFork, House, Question, UsersThree } from '@phosphor-icons/react'; import { DAO_ROUTES } from '../../../../constants/routes'; import { URL_DOCS, URL_FAQ } from '../../../../constants/url'; -import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import Divider from '../../utils/Divider'; import { NavigationLink } from './NavigationLink'; @@ -44,7 +44,7 @@ function ExternalLinks({ closeDrawer }: { closeDrawer?: () => void }) { function InternalLinks({ closeDrawer }: { closeDrawer?: () => void }) { const { safe } = useDaoInfoStore(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const safeAddress = safe?.address; diff --git a/src/hooks/DAO/loaders/useDecentTreasury.ts b/src/hooks/DAO/loaders/useDecentTreasury.ts index 5fe5434765..73eb13b1cf 100644 --- a/src/hooks/DAO/loaders/useDecentTreasury.ts +++ b/src/hooks/DAO/loaders/useDecentTreasury.ts @@ -12,7 +12,7 @@ import { useFractal } from '../../../providers/App/AppProvider'; import useBalancesAPI from '../../../providers/App/hooks/useBalancesAPI'; import { useSafeAPI } from '../../../providers/App/hooks/useSafeAPI'; import { TreasuryAction } from '../../../providers/App/treasury/action'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { TokenEventType, @@ -31,7 +31,7 @@ export const useDecentTreasury = () => { const safeAPI = useSafeAPI(); const { getTokenBalances, getNFTBalances, getDeFiBalances } = useBalancesAPI(); - const { chain, nativeTokenIcon } = useNetworkConfig(); + const { chain, nativeTokenIcon } = useNetworkConfigStore(); const safeAddress = safe?.address; const publicClient = usePublicClient(); diff --git a/src/hooks/DAO/loaders/useFavorites.ts b/src/hooks/DAO/loaders/useFavorites.ts index 0b688608d3..5b36ae7750 100644 --- a/src/hooks/DAO/loaders/useFavorites.ts +++ b/src/hooks/DAO/loaders/useFavorites.ts @@ -1,6 +1,6 @@ import { useState } from 'react'; import { Address } from 'viem'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { CacheKeys, CacheExpiry, FavoritesCacheValue } from '../../utils/cache/cacheDefaults'; import { getValue, setValue } from '../../utils/cache/useLocalStorage'; @@ -8,7 +8,7 @@ export const useAccountFavorites = () => { const [favoritesList, setFavoritesList] = useState( getValue({ cacheName: CacheKeys.FAVORITES }) || [], ); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const toggleFavorite = (address: Address, name: string) => { const favorites = getValue({ cacheName: CacheKeys.FAVORITES }) || []; diff --git a/src/hooks/DAO/loaders/useFractalGovernance.ts b/src/hooks/DAO/loaders/useFractalGovernance.ts index 761eeb5f8b..b843ae12ce 100644 --- a/src/hooks/DAO/loaders/useFractalGovernance.ts +++ b/src/hooks/DAO/loaders/useFractalGovernance.ts @@ -4,7 +4,7 @@ import { DAOQueryDocument } from '../../../../.graphclient'; import { useFractal } from '../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../providers/App/governance/action'; import useIPFSClient from '../../../providers/App/hooks/useIPFSClient'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { GovernanceType, ProposalTemplate } from '../../../types'; import { useERC20LinearStrategy } from './governance/useERC20LinearStrategy'; @@ -38,7 +38,7 @@ export const useFractalGovernance = () => { const ONE_MINUTE = 60 * 1000; - const { subgraph } = useNetworkConfig(); + const { subgraph } = useNetworkConfigStore(); useQuery(DAOQueryDocument, { variables: { safeAddress }, diff --git a/src/hooks/DAO/loaders/useFractalGuardContracts.ts b/src/hooks/DAO/loaders/useFractalGuardContracts.ts index 9600a335ac..61e14095ce 100644 --- a/src/hooks/DAO/loaders/useFractalGuardContracts.ts +++ b/src/hooks/DAO/loaders/useFractalGuardContracts.ts @@ -4,7 +4,7 @@ import { getContract, zeroAddress } from 'viem'; import { usePublicClient } from 'wagmi'; import { useFractal } from '../../../providers/App/AppProvider'; import { GuardContractAction } from '../../../providers/App/guardContracts/action'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { FreezeGuardType, FreezeVotingType } from '../../../types'; import { useAddressContractType } from '../../utils/useAddressContractType'; @@ -19,7 +19,7 @@ export const useFractalGuardContracts = ({ loadOnMount = true }: { loadOnMount?: const safeAddress = safe?.address; - const { chain } = useNetworkConfig(); + const { chain } = useNetworkConfigStore(); const { getAddressContractType } = useAddressContractType(); diff --git a/src/hooks/DAO/loaders/useFractalNode.ts b/src/hooks/DAO/loaders/useFractalNode.ts index 007db1291e..d4c2a3e86e 100644 --- a/src/hooks/DAO/loaders/useFractalNode.ts +++ b/src/hooks/DAO/loaders/useFractalNode.ts @@ -4,7 +4,7 @@ import { Address, getAddress, isAddress } from 'viem'; import { DAOQueryDocument } from '../../../../.graphclient'; import { useFractal } from '../../../providers/App/AppProvider'; import { useSafeAPI } from '../../../providers/App/hooks/useSafeAPI'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { useDecentModules } from './useDecentModules'; @@ -20,7 +20,7 @@ export const useFractalNode = ({ // tracks the current valid Safe address and chain id; helps prevent unnecessary calls const currentValidSafe = useRef(); const [errorLoading, setErrorLoading] = useState(false); - const { subgraph } = useNetworkConfig(); + const { subgraph } = useNetworkConfigStore(); const [getDAOInfo] = useLazyQuery(DAOQueryDocument, { context: { subgraphSpace: subgraph.space, diff --git a/src/hooks/DAO/loaders/useHatsTree.ts b/src/hooks/DAO/loaders/useHatsTree.ts index 0dbe4d0e27..b87155a61e 100644 --- a/src/hooks/DAO/loaders/useHatsTree.ts +++ b/src/hooks/DAO/loaders/useHatsTree.ts @@ -9,7 +9,7 @@ import { StreamsQueryDocument } from '../../../../.graphclient'; import { SablierV2LockupLinearAbi } from '../../../assets/abi/SablierV2LockupLinear'; import { useFractal } from '../../../providers/App/AppProvider'; import useIPFSClient from '../../../providers/App/hooks/useIPFSClient'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { DecentHatsError } from '../../../store/roles/rolesStoreUtils'; import { useRolesStore } from '../../../store/roles/useRolesStore'; @@ -50,7 +50,7 @@ const useHatsTree = () => { hatsAccount1ofNMasterCopy: hatsAccountImplementation, hatsElectionsEligibilityMasterCopy: hatsElectionsImplementation, }, - } = useNetworkConfig(); + } = useNetworkConfigStore(); const publicClient = usePublicClient(); const apolloClient = useApolloClient(); diff --git a/src/hooks/DAO/proposal/useCreateProposalTemplate.ts b/src/hooks/DAO/proposal/useCreateProposalTemplate.ts index b24f73f650..c5cc8063f6 100644 --- a/src/hooks/DAO/proposal/useCreateProposalTemplate.ts +++ b/src/hooks/DAO/proposal/useCreateProposalTemplate.ts @@ -6,7 +6,7 @@ import { normalize } from 'viem/ens'; import { usePublicClient } from 'wagmi'; import { useFractal } from '../../../providers/App/AppProvider'; import useIPFSClient from '../../../providers/App/hooks/useIPFSClient'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { ProposalExecuteData } from '../../../types'; import { CreateProposalForm } from '../../../types/proposalBuilder'; import { validateENSName } from '../../../utils/url'; @@ -28,7 +28,7 @@ export default function useCreateProposalTemplate() { const { contracts: { keyValuePairs }, - } = useNetworkConfig(); + } = useNetworkConfigStore(); const { t } = useTranslation('proposalMetadata'); diff --git a/src/hooks/DAO/proposal/useRemoveProposalTemplate.ts b/src/hooks/DAO/proposal/useRemoveProposalTemplate.ts index b5652ecee4..5b1e12e8c9 100644 --- a/src/hooks/DAO/proposal/useRemoveProposalTemplate.ts +++ b/src/hooks/DAO/proposal/useRemoveProposalTemplate.ts @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'; import { encodeFunctionData } from 'viem'; import { useFractal } from '../../../providers/App/AppProvider'; import useIPFSClient from '../../../providers/App/hooks/useIPFSClient'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { ProposalExecuteData } from '../../../types'; export default function useRemoveProposalTemplate() { @@ -15,7 +15,7 @@ export default function useRemoveProposalTemplate() { const { contracts: { keyValuePairs }, - } = useNetworkConfig(); + } = useNetworkConfigStore(); const { t } = useTranslation('proposalMetadata'); diff --git a/src/hooks/DAO/proposal/useSubmitProposal.ts b/src/hooks/DAO/proposal/useSubmitProposal.ts index 05efdc6f7e..28b10d6e45 100644 --- a/src/hooks/DAO/proposal/useSubmitProposal.ts +++ b/src/hooks/DAO/proposal/useSubmitProposal.ts @@ -21,7 +21,7 @@ import { useFractal } from '../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../providers/App/governance/action'; import useIPFSClient from '../../../providers/App/hooks/useIPFSClient'; import { useSafeAPI } from '../../../providers/App/hooks/useSafeAPI'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { CreateProposalMetadata, MetaTransaction, ProposalExecuteData } from '../../../types'; import { buildSafeApiUrl, getAzoriusModuleFromModules } from '../../../utils'; @@ -102,7 +102,7 @@ export default function useSubmitProposal() { safeBaseURL, addressPrefix, contracts: { multiSendCallOnly }, - } = useNetworkConfig(); + } = useNetworkConfigStore(); const ipfsClient = useIPFSClient(); const pendingProposalAdd = useCallback( diff --git a/src/hooks/DAO/useBuildDAOTx.ts b/src/hooks/DAO/useBuildDAOTx.ts index 3075365a12..0ee80c19cd 100644 --- a/src/hooks/DAO/useBuildDAOTx.ts +++ b/src/hooks/DAO/useBuildDAOTx.ts @@ -3,7 +3,7 @@ import { Address } from 'viem'; import { useAccount, usePublicClient } from 'wagmi'; import { TxBuilderFactory } from '../../models/TxBuilderFactory'; import { useFractal } from '../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { AzoriusERC20DAO, AzoriusERC721DAO, @@ -35,7 +35,7 @@ const useBuildDAOTx = () => { freezeVotingErc721MasterCopy, freezeVotingMultisigMasterCopy, }, - } = useNetworkConfig(); + } = useNetworkConfigStore(); const { governance, diff --git a/src/hooks/DAO/useCreateSubDAOProposal.ts b/src/hooks/DAO/useCreateSubDAOProposal.ts index 0551db3c50..39942b3a14 100644 --- a/src/hooks/DAO/useCreateSubDAOProposal.ts +++ b/src/hooks/DAO/useCreateSubDAOProposal.ts @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'; import { encodeFunctionData, isHex } from 'viem'; import MultiSendCallOnlyAbi from '../../assets/abi/MultiSendCallOnly'; import { useFractal } from '../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore'; import { AzoriusERC20DAO, @@ -28,7 +28,7 @@ export const useCreateSubDAOProposal = () => { const { safe } = useDaoInfoStore(); const { contracts: { multiSendCallOnly, keyValuePairs }, - } = useNetworkConfig(); + } = useNetworkConfigStore(); const azoriusGovernance = governance as AzoriusGovernance; const safeAddress = safe?.address; diff --git a/src/hooks/DAO/useDeployAzorius.ts b/src/hooks/DAO/useDeployAzorius.ts index de40bd3978..65a96caa9f 100644 --- a/src/hooks/DAO/useDeployAzorius.ts +++ b/src/hooks/DAO/useDeployAzorius.ts @@ -11,7 +11,7 @@ import { SENTINEL_ADDRESS } from '../../constants/common'; import { DAO_ROUTES } from '../../constants/routes'; import { TxBuilderFactory } from '../../models/TxBuilderFactory'; import { useSafeAPI } from '../../providers/App/hooks/useSafeAPI'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore'; import { AzoriusERC20DAO, @@ -51,7 +51,7 @@ const useDeployAzorius = () => { freezeVotingMultisigMasterCopy, }, addressPrefix, - } = useNetworkConfig(); + } = useNetworkConfigStore(); const { safe, subgraphInfo } = useDaoInfoStore(); const { t } = useTranslation(['transaction', 'proposalMetadata']); diff --git a/src/hooks/DAO/useDeployDAO.ts b/src/hooks/DAO/useDeployDAO.ts index 62c50138a1..9cbfed3c52 100644 --- a/src/hooks/DAO/useDeployDAO.ts +++ b/src/hooks/DAO/useDeployDAO.ts @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'; import { Address, getContract, isHex } from 'viem'; import { useWalletClient } from 'wagmi'; import MultiSendCallOnlyAbi from '../../assets/abi/MultiSendCallOnly'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { SafeMultisigDAO, AzoriusERC20DAO, AzoriusERC721DAO } from '../../types'; import { useTransaction } from '../utils/useTransaction'; import useBuildDAOTx from './useBuildDAOTx'; @@ -17,7 +17,7 @@ const useDeployDAO = () => { const { addressPrefix, contracts: { multiSendCallOnly }, - } = useNetworkConfig(); + } = useNetworkConfigStore(); const { data: walletClient } = useWalletClient(); diff --git a/src/hooks/DAO/useKeyValuePairs.ts b/src/hooks/DAO/useKeyValuePairs.ts index 38615ef52c..dd313a38a8 100644 --- a/src/hooks/DAO/useKeyValuePairs.ts +++ b/src/hooks/DAO/useKeyValuePairs.ts @@ -5,7 +5,7 @@ import { useSearchParams } from 'react-router-dom'; import { Address, GetContractEventsReturnType, getContract } from 'viem'; import { usePublicClient } from 'wagmi'; import { logError } from '../../helpers/errorLogging'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore'; import { useRolesStore } from '../../store/roles/useRolesStore'; @@ -97,7 +97,7 @@ const useKeyValuePairs = () => { const { chain, contracts: { keyValuePairs, sablierV2LockupLinear }, - } = useNetworkConfig(); + } = useNetworkConfigStore(); const { setHatKeyValuePairData } = useRolesStore(); const [searchParams] = useSearchParams(); diff --git a/src/hooks/DAO/useParseSafeAddress.ts b/src/hooks/DAO/useParseSafeAddress.ts index fe5713527d..19f0b76899 100644 --- a/src/hooks/DAO/useParseSafeAddress.ts +++ b/src/hooks/DAO/useParseSafeAddress.ts @@ -1,7 +1,7 @@ import { useSearchParams } from 'react-router-dom'; import { isAddress } from 'viem'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; import { validPrefixes } from '../../providers/NetworkConfig/networks'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; export const useParseSafeAddress = () => { const [searchParams] = useSearchParams(); @@ -10,7 +10,7 @@ export const useParseSafeAddress = () => { const queryAddressPrefix = queryPrefixAndAddress?.[0]; const queryDaoAddress = queryPrefixAndAddress?.[1]; - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); if ( queryAddressPrefix === undefined || diff --git a/src/hooks/DAO/useSearchDao.ts b/src/hooks/DAO/useSearchDao.ts index 59f193f586..74a42a57d2 100644 --- a/src/hooks/DAO/useSearchDao.ts +++ b/src/hooks/DAO/useSearchDao.ts @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { useIsSafe } from '../safe/useIsSafe'; import useAddress from '../utils/useAddress'; @@ -11,7 +11,7 @@ export const useSearchDao = () => { const { address, isValid, isLoading: isAddressLoading } = useAddress(searchString); const { isSafe, isSafeLoading } = useIsSafe(address); const { t } = useTranslation('dashboard'); - const { chain } = useNetworkConfig(); + const { chain } = useNetworkConfigStore(); const isLoading = isAddressLoading === true || isSafeLoading === true; diff --git a/src/hooks/schemas/DAOCreate/useDAOCreateTests.ts b/src/hooks/schemas/DAOCreate/useDAOCreateTests.ts index 7b86d4075b..34be96500c 100644 --- a/src/hooks/schemas/DAOCreate/useDAOCreateTests.ts +++ b/src/hooks/schemas/DAOCreate/useDAOCreateTests.ts @@ -4,7 +4,7 @@ import { isAddress, erc20Abi, getContract } from 'viem'; import { usePublicClient } from 'wagmi'; import { AnyObject } from 'yup'; import { logError } from '../../../helpers/errorLogging'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { AddressValidationMap, CreatorFormState, TokenAllocation } from '../../../types'; import { validateENSName } from '../../../utils/url'; import { validateAddress } from '../common/useValidationAddress'; @@ -22,7 +22,7 @@ export function useDAOCreateTests() { const addressValidationMap = useRef(new Map()); const { t } = useTranslation(['daoCreate', 'common']); const publicClient = usePublicClient(); - const { chain } = useNetworkConfig(); + const { chain } = useNetworkConfigStore(); const minValueValidation = useMemo( () => (minValue: number) => { diff --git a/src/hooks/schemas/common/useValidationAddress.tsx b/src/hooks/schemas/common/useValidationAddress.tsx index d3e0e69038..7c9718e46b 100644 --- a/src/hooks/schemas/common/useValidationAddress.tsx +++ b/src/hooks/schemas/common/useValidationAddress.tsx @@ -4,7 +4,7 @@ import { Address, PublicClient, getAddress, isAddress } from 'viem'; import { normalize } from 'viem/ens'; import { usePublicClient } from 'wagmi'; import { AnyObject } from 'yup'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { AddressValidationMap, ERC721TokenConfig } from '../../../types'; import { validateENSName } from '../../../utils/url'; @@ -73,7 +73,7 @@ export const useValidationAddress = () => { const { t } = useTranslation(['daoCreate', 'common', 'modals']); const { safe } = useDaoInfoStore(); - const { chain } = useNetworkConfig(); + const { chain } = useNetworkConfigStore(); const publicClient = usePublicClient(); diff --git a/src/hooks/stake/lido/useLidoStaking.ts b/src/hooks/stake/lido/useLidoStaking.ts index 48f051cfff..fa289acc26 100644 --- a/src/hooks/stake/lido/useLidoStaking.ts +++ b/src/hooks/stake/lido/useLidoStaking.ts @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'; import { encodeFunctionData } from 'viem'; import LidoStEthAbi from '../../../assets/abi/LidoStEthAbi'; import LidoWithdrawalQueueAbi from '../../../assets/abi/LidoWithdrawalQueueAbi'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { ProposalExecuteData } from '../../../types'; import useSubmitProposal from '../../DAO/proposal/useSubmitProposal'; @@ -12,7 +12,7 @@ export default function useLidoStaking() { const { safe } = useDaoInfoStore(); const { staking: { lido }, - } = useNetworkConfig(); + } = useNetworkConfigStore(); const { submitProposal } = useSubmitProposal(); const { t } = useTranslation('proposal'); diff --git a/src/hooks/streams/useCreateSablierStream.ts b/src/hooks/streams/useCreateSablierStream.ts index 91e6a89bce..6e36ccce0d 100644 --- a/src/hooks/streams/useCreateSablierStream.ts +++ b/src/hooks/streams/useCreateSablierStream.ts @@ -4,7 +4,7 @@ import { useCallback } from 'react'; import { Address, Hex, encodeFunctionData, erc20Abi, getAddress, zeroAddress } from 'viem'; import GnosisSafeL2 from '../../assets/abi/GnosisSafeL2'; import SablierV2BatchAbi from '../../assets/abi/SablierV2Batch'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore'; import { PreparedNewStreamData } from '../../types/roles'; import { SENTINEL_MODULE } from '../../utils/address'; @@ -19,7 +19,7 @@ export function convertStreamIdToBigInt(streamId: string) { export default function useCreateSablierStream() { const { contracts: { sablierV2LockupLinear, sablierV2Batch, decentSablierStreamManagementModule }, - } = useNetworkConfig(); + } = useNetworkConfigStore(); const { safe } = useDaoInfoStore(); const safeAddress = safe?.address; diff --git a/src/hooks/utils/cache/useLocalDB.ts b/src/hooks/utils/cache/useLocalDB.ts index 1fb3a1ad91..f8b96f5871 100644 --- a/src/hooks/utils/cache/useLocalDB.ts +++ b/src/hooks/utils/cache/useLocalDB.ts @@ -1,6 +1,6 @@ import { useCallback } from 'react'; import { logError } from '../../../helpers/errorLogging'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { CacheExpiry, CACHE_DEFAULTS, IStorageValue, keyInternal } from './cacheDefaults'; /** @@ -131,7 +131,7 @@ export const getIndexedDBValue = async ( }; export const useIndexedDB = (objectStoreName: string) => { - const { chain } = useNetworkConfig(); + const { chain } = useNetworkConfigStore(); const set = useCallback( async (key: string, value: any, expirationMinutes: number = CacheExpiry.ONE_WEEK) => { diff --git a/src/hooks/utils/useAutomaticSwitchChain.ts b/src/hooks/utils/useAutomaticSwitchChain.ts index 570c3b5095..e738b67b8a 100644 --- a/src/hooks/utils/useAutomaticSwitchChain.ts +++ b/src/hooks/utils/useAutomaticSwitchChain.ts @@ -2,7 +2,7 @@ import { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { toast } from 'sonner'; import { useSwitchChain } from 'wagmi'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { getChainIdFromPrefix } from '../../utils/url'; export const useAutomaticSwitchChain = ({ @@ -11,7 +11,7 @@ export const useAutomaticSwitchChain = ({ addressPrefix: string | undefined; }) => { const { t } = useTranslation(['common']); - const networkConfig = useNetworkConfig(); + const networkConfig = useNetworkConfigStore(); const { switchChain } = useSwitchChain({ mutation: { onSuccess: () => { diff --git a/src/hooks/utils/useAvatar.ts b/src/hooks/utils/useAvatar.ts index 8712b60dff..f491a71a1e 100644 --- a/src/hooks/utils/useAvatar.ts +++ b/src/hooks/utils/useAvatar.ts @@ -1,8 +1,8 @@ import { useEnsAvatar } from 'wagmi'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; const useAvatar = (name: string) => { - const { chain } = useNetworkConfig(); + const { chain } = useNetworkConfigStore(); const { data: avatarURL } = useEnsAvatar({ name, chainId: chain.id, diff --git a/src/hooks/utils/useCreateRoles.ts b/src/hooks/utils/useCreateRoles.ts index a8b27ac658..1a4b6353d3 100644 --- a/src/hooks/utils/useCreateRoles.ts +++ b/src/hooks/utils/useCreateRoles.ts @@ -35,7 +35,7 @@ import { getRandomBytes } from '../../helpers'; import { generateContractByteCodeLinear } from '../../models/helpers/utils'; import { useFractal } from '../../providers/App/AppProvider'; import useIPFSClient from '../../providers/App/hooks/useIPFSClient'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore'; import { useRolesStore } from '../../store/roles/useRolesStore'; import { @@ -110,7 +110,7 @@ export default function useCreateRoles() { decentAutonomousAdminV1MasterCopy, hatsElectionsEligibilityMasterCopy, }, - } = useNetworkConfig(); + } = useNetworkConfigStore(); const { t } = useTranslation(['roles', 'navigation', 'modals', 'common']); diff --git a/src/hooks/utils/useGetSafeName.ts b/src/hooks/utils/useGetSafeName.ts index e7598543ca..89652c3dfe 100644 --- a/src/hooks/utils/useGetSafeName.ts +++ b/src/hooks/utils/useGetSafeName.ts @@ -3,7 +3,7 @@ import { Address, ChainDoesNotSupportContract, PublicClient } from 'viem'; import { usePublicClient } from 'wagmi'; import { DAOQueryDocument } from '../../../.graphclient'; import graphQLClient from '../../graphql'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { createAccountSubstring } from './useGetAccountName'; export const getSafeName = async ( @@ -44,8 +44,8 @@ export const getSafeName = async ( }; export const useGetSafeName = (chainId?: number) => { + const { getConfigByChainId } = useNetworkConfigStore(); const publicClient = usePublicClient({ chainId }); - const { subgraph } = useNetworkConfig(chainId); return { getSafeName: useCallback( @@ -53,9 +53,9 @@ export const useGetSafeName = (chainId?: number) => { if (!publicClient) { throw new Error('Public client not available'); } - return getSafeName(publicClient, subgraph, address); + return getSafeName(publicClient, getConfigByChainId(chainId).subgraph, address); }, - [publicClient, subgraph], + [publicClient, getConfigByChainId, chainId], ), }; }; diff --git a/src/hooks/utils/useSafeDecoder.tsx b/src/hooks/utils/useSafeDecoder.tsx index 49c3d0aa80..af2b379ef2 100644 --- a/src/hooks/utils/useSafeDecoder.tsx +++ b/src/hooks/utils/useSafeDecoder.tsx @@ -1,7 +1,7 @@ import axios from 'axios'; import { useCallback } from 'react'; import { Address, encodePacked, keccak256 } from 'viem'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { DecodedTransaction, DecodedTxParam } from '../../types'; import { buildSafeApiUrl, parseMultiSendTransactions } from '../../utils'; import { CacheKeys } from './cache/cacheDefaults'; @@ -10,7 +10,7 @@ import { DBObjectKeys, useIndexedDB } from './cache/useLocalDB'; * Handles decoding and caching transactions via the Safe API. */ export const useSafeDecoder = () => { - const { safeBaseURL } = useNetworkConfig(); + const { safeBaseURL } = useNetworkConfigStore(); const [setValue, getValue] = useIndexedDB(DBObjectKeys.DECODED_TRANSACTIONS); const decode = useCallback( async (value: string, to: Address, data?: string): Promise => { diff --git a/src/main.tsx b/src/main.tsx index f5f6a1eeff..2a8e7e7745 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -7,12 +7,15 @@ import './assets/css/sentry.css'; import './assets/css/Toast.css'; import './insights'; import { runMigrations } from './hooks/utils/cache/runMigrations'; -import { useNetworkConfig } from './providers/NetworkConfig/NetworkConfigProvider'; +import { useSetNetworkConfig } from './providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from './providers/NetworkConfig/useNetworkConfigStore'; import Providers from './providers/Providers'; import { router } from './router'; function DecentRouterProvider() { - const { addressPrefix } = useNetworkConfig(); + useSetNetworkConfig(); + + const { addressPrefix } = useNetworkConfigStore(); const urlParams = new URLSearchParams(window.location.search); const addressWithPrefix = urlParams.get('dao'); diff --git a/src/pages/LoadingProblem.tsx b/src/pages/LoadingProblem.tsx index 5d4875c344..f755972745 100644 --- a/src/pages/LoadingProblem.tsx +++ b/src/pages/LoadingProblem.tsx @@ -5,10 +5,10 @@ import { StetoscopeIllustrationDesktop, } from '../components/ui/icons/Icons'; import { CONTENT_MAXW } from '../constants/common'; -import { useNetworkConfig } from '../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../providers/NetworkConfig/useNetworkConfigStore'; function LoadingProblem({ type }: { type: 'invalidSafe' | 'wrongNetwork' | 'badQueryParam' }) { - const { chain } = useNetworkConfig(); + const { chain } = useNetworkConfigStore(); const { t } = useTranslation('common'); return ( diff --git a/src/pages/dao/edit/governance/SafeEditGovernancePage.tsx b/src/pages/dao/edit/governance/SafeEditGovernancePage.tsx index beea9d107c..51898fe474 100644 --- a/src/pages/dao/edit/governance/SafeEditGovernancePage.tsx +++ b/src/pages/dao/edit/governance/SafeEditGovernancePage.tsx @@ -13,7 +13,7 @@ import { DAO_ROUTES } from '../../../../constants/routes'; import useDeployAzorius from '../../../../hooks/DAO/useDeployAzorius'; import { analyticsEvents } from '../../../../insights/analyticsEvents'; import { useFractal } from '../../../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import { AzoriusERC20DAO, @@ -33,7 +33,7 @@ export function SafeEditGovernancePage() { } = useFractal(); const user = useAccount(); const { safe, subgraphInfo } = useDaoInfoStore(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { t } = useTranslation(['daoEdit', 'common', 'breadcrumbs']); const navigate = useNavigate(); const isMultisig = type === GovernanceType.MULTISIG; diff --git a/src/pages/dao/hierarchy/SafeHierarchyPage.tsx b/src/pages/dao/hierarchy/SafeHierarchyPage.tsx index ef1dcf7a8c..0cee833161 100644 --- a/src/pages/dao/hierarchy/SafeHierarchyPage.tsx +++ b/src/pages/dao/hierarchy/SafeHierarchyPage.tsx @@ -10,7 +10,7 @@ import { useHeaderHeight } from '../../../constants/common'; import { DAO_ROUTES } from '../../../constants/routes'; import { useCanUserCreateProposal } from '../../../hooks/utils/useCanUserSubmitProposal'; import { analyticsEvents } from '../../../insights/analyticsEvents'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; export function SafeHierarchyPage() { @@ -24,7 +24,7 @@ export function SafeHierarchyPage() { const HEADER_HEIGHT = useHeaderHeight(); const navigate = useNavigate(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { canUserCreateProposal } = useCanUserCreateProposal(); diff --git a/src/pages/dao/proposal-templates/SafeProposalTemplatesPage.tsx b/src/pages/dao/proposal-templates/SafeProposalTemplatesPage.tsx index 8349f7d7dd..2369541965 100644 --- a/src/pages/dao/proposal-templates/SafeProposalTemplatesPage.tsx +++ b/src/pages/dao/proposal-templates/SafeProposalTemplatesPage.tsx @@ -12,7 +12,7 @@ import { DAO_ROUTES } from '../../../constants/routes'; import { useCanUserCreateProposal } from '../../../hooks/utils/useCanUserSubmitProposal'; import { analyticsEvents } from '../../../insights/analyticsEvents'; import { useFractal } from '../../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; export function SafeProposalTemplatesPage() { @@ -26,7 +26,7 @@ export function SafeProposalTemplatesPage() { } = useFractal(); const { safe } = useDaoInfoStore(); const { canUserCreateProposal } = useCanUserCreateProposal(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const safeAddress = safe?.address; diff --git a/src/pages/dao/proposals/[proposalId]/index.tsx b/src/pages/dao/proposals/[proposalId]/index.tsx index 0dae6bebdc..b60e166931 100644 --- a/src/pages/dao/proposals/[proposalId]/index.tsx +++ b/src/pages/dao/proposals/[proposalId]/index.tsx @@ -14,7 +14,7 @@ import useSnapshotProposal from '../../../../hooks/DAO/loaders/snapshot/useSnaps import { useGetMetadata } from '../../../../hooks/DAO/proposal/useGetMetadata'; import { analyticsEvents } from '../../../../insights/analyticsEvents'; import { useFractal } from '../../../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import { AzoriusProposal, SnapshotProposal } from '../../../../types'; @@ -29,7 +29,7 @@ export function SafeProposalDetailsPage() { governance: { proposals, loadingProposals, allProposalsLoaded, isAzorius }, } = useFractal(); const { safe } = useDaoInfoStore(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { proposalId } = useParams(); // Either the proposals have not even started loading yet, or not all proposals have been loaded, so this could be one of them diff --git a/src/pages/dao/proposals/new/sablier/SafeSablierProposalCreatePage.tsx b/src/pages/dao/proposals/new/sablier/SafeSablierProposalCreatePage.tsx index 704e107e1b..b986be6259 100644 --- a/src/pages/dao/proposals/new/sablier/SafeSablierProposalCreatePage.tsx +++ b/src/pages/dao/proposals/new/sablier/SafeSablierProposalCreatePage.tsx @@ -71,7 +71,7 @@ import useSubmitProposal from '../../../../../hooks/DAO/proposal/useSubmitPropos import { useCanUserCreateProposal } from '../../../../../hooks/utils/useCanUserSubmitProposal'; import { analyticsEvents } from '../../../../../insights/analyticsEvents'; import { useFractal } from '../../../../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../../store/daoInfo/useDaoInfoStore'; import { BigIntValuePair, CreateProposalSteps } from '../../../../../types'; import { scrollToBottom } from '../../../../../utils/ui'; @@ -763,7 +763,7 @@ export function SafeSablierProposalCreatePage() { const { addressPrefix, contracts: { sablierV2Batch, sablierV2LockupTranched }, - } = useNetworkConfig(); + } = useNetworkConfigStore(); const navigate = useNavigate(); const { t } = useTranslation(['proposalTemplate', 'proposal']); const [title, setTitle] = useState(''); diff --git a/src/pages/dao/roles/SafeRolesPage.tsx b/src/pages/dao/roles/SafeRolesPage.tsx index 8669e12282..9e59436852 100644 --- a/src/pages/dao/roles/SafeRolesPage.tsx +++ b/src/pages/dao/roles/SafeRolesPage.tsx @@ -13,7 +13,7 @@ import PageHeader from '../../../components/ui/page/Header/PageHeader'; import { DAO_ROUTES } from '../../../constants/routes'; import { useCanUserCreateProposal } from '../../../hooks/utils/useCanUserSubmitProposal'; import { analyticsEvents } from '../../../insights/analyticsEvents'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { useRolesStore } from '../../../store/roles/useRolesStore'; @@ -23,7 +23,7 @@ export function SafeRolesPage() { }, []); const { hatsTree } = useRolesStore(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { t } = useTranslation(['roles']); const { safe } = useDaoInfoStore(); const navigate = useNavigate(); diff --git a/src/pages/dao/roles/details/SafeRoleDetailsPage.tsx b/src/pages/dao/roles/details/SafeRoleDetailsPage.tsx index 51789f8f31..799b69d63e 100644 --- a/src/pages/dao/roles/details/SafeRoleDetailsPage.tsx +++ b/src/pages/dao/roles/details/SafeRoleDetailsPage.tsx @@ -3,14 +3,14 @@ import { useNavigate, useSearchParams } from 'react-router-dom'; import RolesDetailsDrawer from '../../../../components/Roles/RolesDetailsDrawer'; import RolesDetailsDrawerMobile from '../../../../components/Roles/RolesDetailsDrawerMobile'; import { DAO_ROUTES } from '../../../../constants/routes'; -import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import { useRolesStore } from '../../../../store/roles/useRolesStore'; export function SafeRoleDetailsPage() { const { safe } = useDaoInfoStore(); const navigate = useNavigate(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { hatsTree } = useRolesStore(); const [searchParams] = useSearchParams(); diff --git a/src/pages/dao/roles/edit/SafeRolesEditPage.tsx b/src/pages/dao/roles/edit/SafeRolesEditPage.tsx index 37a61f4e91..6f2d725908 100644 --- a/src/pages/dao/roles/edit/SafeRolesEditPage.tsx +++ b/src/pages/dao/roles/edit/SafeRolesEditPage.tsx @@ -20,7 +20,7 @@ import { useRolesSchema } from '../../../../hooks/schemas/roles/useRolesSchema'; import useCreateRoles from '../../../../hooks/utils/useCreateRoles'; import { useNavigationBlocker } from '../../../../hooks/utils/useNavigationBlocker'; import { analyticsEvents } from '../../../../insights/analyticsEvents'; -import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import { useRolesStore } from '../../../../store/roles/useRolesStore'; import { EditBadgeStatus, RoleFormValues } from '../../../../types/roles'; @@ -32,7 +32,7 @@ export function SafeRolesEditPage() { const { t } = useTranslation(['roles', 'navigation', 'modals', 'common']); const { safe } = useDaoInfoStore(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { rolesSchema } = useRolesSchema(); const { hatsTree } = useRolesStore(); diff --git a/src/pages/dao/roles/edit/details/SafeRoleEditDetailsPage.tsx b/src/pages/dao/roles/edit/details/SafeRoleEditDetailsPage.tsx index 77f60a98ed..e31a38d22b 100644 --- a/src/pages/dao/roles/edit/details/SafeRoleEditDetailsPage.tsx +++ b/src/pages/dao/roles/edit/details/SafeRoleEditDetailsPage.tsx @@ -31,7 +31,7 @@ import { } from '../../../../../constants/common'; import { DAO_ROUTES } from '../../../../../constants/routes'; import { useNavigationBlocker } from '../../../../../hooks/utils/useNavigationBlocker'; -import { useNetworkConfig } from '../../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../../store/daoInfo/useDaoInfoStore'; import { useRolesStore } from '../../../../../store/roles/useRolesStore'; import { @@ -133,7 +133,7 @@ export function SafeRoleEditDetailsPage() { const { t } = useTranslation(['roles']); const { safe } = useDaoInfoStore(); const { getPayment } = useRolesStore(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const navigate = useNavigate(); const { values, setFieldValue, touched, setTouched } = useFormikContext(); const [searchParams] = useSearchParams(); diff --git a/src/pages/dao/roles/edit/summary/SafeRolesEditProposalSummaryPage.tsx b/src/pages/dao/roles/edit/summary/SafeRolesEditProposalSummaryPage.tsx index 6452a124ff..f7f6856d9c 100644 --- a/src/pages/dao/roles/edit/summary/SafeRolesEditProposalSummaryPage.tsx +++ b/src/pages/dao/roles/edit/summary/SafeRolesEditProposalSummaryPage.tsx @@ -8,7 +8,7 @@ import RoleFormCreateProposal from '../../../../../components/Roles/forms/RoleFo import PageHeader from '../../../../../components/ui/page/Header/PageHeader'; import { SIDEBAR_WIDTH, useHeaderHeight } from '../../../../../constants/common'; import { DAO_ROUTES } from '../../../../../constants/routes'; -import { useNetworkConfig } from '../../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../../store/daoInfo/useDaoInfoStore'; import { RoleFormValues } from '../../../../../types/roles'; @@ -17,7 +17,7 @@ export function SafeRolesEditProposalSummaryPage() { const navigate = useNavigate(); const { safe } = useDaoInfoStore(); const { t } = useTranslation(['roles', 'breadcrumbs']); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { values } = useFormikContext(); const safeAddress = safe?.address; diff --git a/src/pages/dao/settings/general/SafeGeneralSettingsPage.tsx b/src/pages/dao/settings/general/SafeGeneralSettingsPage.tsx index 8311e60aed..43938c0fe2 100644 --- a/src/pages/dao/settings/general/SafeGeneralSettingsPage.tsx +++ b/src/pages/dao/settings/general/SafeGeneralSettingsPage.tsx @@ -13,7 +13,7 @@ import { DAO_ROUTES } from '../../../../constants/routes'; import useSubmitProposal from '../../../../hooks/DAO/proposal/useSubmitProposal'; import { useCanUserCreateProposal } from '../../../../hooks/utils/useCanUserSubmitProposal'; import { createAccountSubstring } from '../../../../hooks/utils/useGetAccountName'; -import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import { ProposalExecuteData } from '../../../../types'; import { validateENSName } from '../../../../utils/url'; @@ -31,7 +31,7 @@ export function SafeGeneralSettingsPage() { const { addressPrefix, contracts: { keyValuePairs }, - } = useNetworkConfig(); + } = useNetworkConfigStore(); const safeAddress = safe?.address; diff --git a/src/pages/dao/settings/governance/SafeGovernanceSettingsPage.tsx b/src/pages/dao/settings/governance/SafeGovernanceSettingsPage.tsx index 035cd536c9..718c4d2e41 100644 --- a/src/pages/dao/settings/governance/SafeGovernanceSettingsPage.tsx +++ b/src/pages/dao/settings/governance/SafeGovernanceSettingsPage.tsx @@ -10,13 +10,13 @@ import { StyledBox } from '../../../../components/ui/containers/StyledBox'; import NestedPageHeader from '../../../../components/ui/page/Header/NestedPageHeader'; import { DAO_ROUTES } from '../../../../constants/routes'; import { useFractal } from '../../../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import { GovernanceType } from '../../../../types'; export function SafeGovernanceSettingsPage() { const { t } = useTranslation('settings'); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { governance: { type }, } = useFractal(); diff --git a/src/pages/dao/settings/modules-and-guard/SafeModulesSettingsPage.tsx b/src/pages/dao/settings/modules-and-guard/SafeModulesSettingsPage.tsx index e4da7a80e0..f5e2af63a7 100644 --- a/src/pages/dao/settings/modules-and-guard/SafeModulesSettingsPage.tsx +++ b/src/pages/dao/settings/modules-and-guard/SafeModulesSettingsPage.tsx @@ -9,13 +9,13 @@ import Divider from '../../../../components/ui/utils/Divider'; import { DAO_ROUTES } from '../../../../constants/routes'; import { createAccountSubstring } from '../../../../hooks/utils/useGetAccountName'; import { useFractal } from '../../../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import { FractalModuleType } from '../../../../types'; export function SafeModulesSettingsPage() { const { t } = useTranslation('settings'); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { guardContracts: { freezeGuardContractAddress, freezeVotingContractAddress }, } = useFractal(); diff --git a/src/pages/dao/settings/permissions/SafePermissionsCreateProposal.tsx b/src/pages/dao/settings/permissions/SafePermissionsCreateProposal.tsx index 96476a6d80..e541baf0c8 100644 --- a/src/pages/dao/settings/permissions/SafePermissionsCreateProposal.tsx +++ b/src/pages/dao/settings/permissions/SafePermissionsCreateProposal.tsx @@ -27,7 +27,7 @@ import { DAO_ROUTES } from '../../../../constants/routes'; import { getRandomBytes } from '../../../../helpers'; import { generateContractByteCodeLinear } from '../../../../models/helpers/utils'; import { useFractal } from '../../../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useProposalActionsStore } from '../../../../store/actions/useProposalActionsStore'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import { @@ -47,7 +47,7 @@ export function SafePermissionsCreateProposal() { linearVotingErc721MasterCopy, zodiacModuleProxyFactory, }, - } = useNetworkConfig(); + } = useNetworkConfigStore(); const [searchParams] = useSearchParams(); const votingStrategyAddress = searchParams.get('votingStrategy'); const navigate = useNavigate(); diff --git a/src/pages/dao/settings/permissions/SafePermissionsSettingsPage.tsx b/src/pages/dao/settings/permissions/SafePermissionsSettingsPage.tsx index 2f426253a1..cc4ad181de 100644 --- a/src/pages/dao/settings/permissions/SafePermissionsSettingsPage.tsx +++ b/src/pages/dao/settings/permissions/SafePermissionsSettingsPage.tsx @@ -15,14 +15,14 @@ import { NEUTRAL_2_84 } from '../../../../constants/common'; import { DAO_ROUTES } from '../../../../constants/routes'; import { useCanUserCreateProposal } from '../../../../hooks/utils/useCanUserSubmitProposal'; import { useFractal } from '../../../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import { AzoriusGovernance } from '../../../../types'; export function SafePermissionsSettingsPage() { const { t } = useTranslation(['settings', 'common']); const navigate = useNavigate(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const { governance, governanceContracts: { isLoaded, linearVotingErc20Address }, diff --git a/src/pages/home/SafeDisplayRow.tsx b/src/pages/home/SafeDisplayRow.tsx index acf4516e4b..6f668f8d45 100644 --- a/src/pages/home/SafeDisplayRow.tsx +++ b/src/pages/home/SafeDisplayRow.tsx @@ -9,7 +9,7 @@ import { DAO_ROUTES } from '../../constants/routes'; import useAvatar from '../../hooks/utils/useAvatar'; import { createAccountSubstring } from '../../hooks/utils/useGetAccountName'; import { useGetSafeName } from '../../hooks/utils/useGetSafeName'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { getChainIdFromPrefix, getChainName, getNetworkIcon } from '../../utils/url'; interface SafeDisplayRowProps { @@ -27,7 +27,7 @@ export function SafeDisplayRow({ showAddress, name, }: SafeDisplayRowProps) { - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const navigate = useNavigate(); const { switchChain } = useSwitchChain({ diff --git a/src/providers/App/hooks/useBalancesAPI.ts b/src/providers/App/hooks/useBalancesAPI.ts index e347874403..1b5220cdbe 100644 --- a/src/providers/App/hooks/useBalancesAPI.ts +++ b/src/providers/App/hooks/useBalancesAPI.ts @@ -2,13 +2,13 @@ import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { Address } from 'viem'; import { DefiBalance, NFTBalance, TokenBalance } from '../../../types'; -import { useNetworkConfig } from '../../NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../NetworkConfig/useNetworkConfigStore'; export default function useBalancesAPI() { const { chain, moralis: { deFiSupported }, - } = useNetworkConfig(); + } = useNetworkConfigStore(); const { t } = useTranslation('treasury'); const getTokenBalances = useCallback( diff --git a/src/providers/App/hooks/useSafeAPI.ts b/src/providers/App/hooks/useSafeAPI.ts index 014e08dcaa..3559387c39 100644 --- a/src/providers/App/hooks/useSafeAPI.ts +++ b/src/providers/App/hooks/useSafeAPI.ts @@ -15,7 +15,7 @@ import { setIndexedDBValue, } from '../../../hooks/utils/cache/useLocalDB'; import { SafeWithNextNonce } from '../../../types'; -import { useNetworkConfig } from '../../NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../NetworkConfig/useNetworkConfigStore'; class EnhancedSafeApiKit extends SafeApiKit { readonly CHAINID: number; @@ -103,7 +103,7 @@ class EnhancedSafeApiKit extends SafeApiKit { } export function useSafeAPI() { - const { chain } = useNetworkConfig(); + const { chain } = useNetworkConfigStore(); const safeAPI = useMemo(() => { return new EnhancedSafeApiKit({ chainId: BigInt(chain.id) }); diff --git a/src/providers/NetworkConfig/web3-modal.config.ts b/src/providers/NetworkConfig/web3-modal.config.ts index bc155fff64..f721c48b14 100644 --- a/src/providers/NetworkConfig/web3-modal.config.ts +++ b/src/providers/NetworkConfig/web3-modal.config.ts @@ -5,7 +5,7 @@ import { HttpTransport } from 'viem'; import { http } from 'wagmi'; import { Chain } from 'wagmi/chains'; import { NetworkConfig } from '../../types/network'; -import { supportedNetworks } from './NetworkConfigProvider'; +import { supportedNetworks } from './useNetworkConfigStore'; const supportedWagmiChains = supportedNetworks.map(network => network.chain); diff --git a/src/utils/url.ts b/src/utils/url.ts index ab42df238e..9edc366177 100644 --- a/src/utils/url.ts +++ b/src/utils/url.ts @@ -1,5 +1,5 @@ import { normalize } from 'viem/ens'; -import { supportedNetworks } from '../providers/NetworkConfig/NetworkConfigProvider'; +import { supportedNetworks } from '../providers/NetworkConfig/useNetworkConfigStore'; export const isValidUrl = (urlString: string) => { try { const url = new URL(urlString); From 19154788ff52d61ee930e7a929150709eafac959 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:21:35 -0500 Subject: [PATCH 03/48] Remove unused network configuration hook and clean up imports --- src/main.tsx | 3 --- .../NetworkConfig/NetworkConfigProvider.tsx | 19 ------------------- 2 files changed, 22 deletions(-) delete mode 100644 src/providers/NetworkConfig/NetworkConfigProvider.tsx diff --git a/src/main.tsx b/src/main.tsx index 2a8e7e7745..10e805db45 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -7,14 +7,11 @@ import './assets/css/sentry.css'; import './assets/css/Toast.css'; import './insights'; import { runMigrations } from './hooks/utils/cache/runMigrations'; -import { useSetNetworkConfig } from './providers/NetworkConfig/NetworkConfigProvider'; import { useNetworkConfigStore } from './providers/NetworkConfig/useNetworkConfigStore'; import Providers from './providers/Providers'; import { router } from './router'; function DecentRouterProvider() { - useSetNetworkConfig(); - const { addressPrefix } = useNetworkConfigStore(); const urlParams = new URLSearchParams(window.location.search); const addressWithPrefix = urlParams.get('dao'); diff --git a/src/providers/NetworkConfig/NetworkConfigProvider.tsx b/src/providers/NetworkConfig/NetworkConfigProvider.tsx deleted file mode 100644 index 2ce3cc619a..0000000000 --- a/src/providers/NetworkConfig/NetworkConfigProvider.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { useEffect } from 'react'; -import { useChainId } from 'wagmi'; -import { useNetworkConfigStore } from './useNetworkConfigStore'; - -// Custom hook to use the network config -export const useSetNetworkConfig = () => { - const { getConfigByChainId, setCurrentConfig } = useNetworkConfigStore(); - const currentChainId = useChainId(); - - // Update currentConfig when chainId changes - useEffect(() => { - try { - const newConfig = getConfigByChainId(currentChainId); - setCurrentConfig(newConfig); - } catch (error) { - console.error(error); - } - }, [currentChainId, getConfigByChainId, setCurrentConfig]); -}; From dae8741aae8a2471bcf05bc347e7cd07b04396e2 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:21:42 -0500 Subject: [PATCH 04/48] Refactor SafeMenuItem and related hooks to simplify network handling and remove unused chain switching logic --- .../ui/menus/SafesMenu/SafeMenuItem.tsx | 19 ++---------- src/hooks/DAO/loaders/useFractalNode.ts | 6 ++-- src/hooks/utils/useAutomaticSwitchChain.ts | 29 ++++--------------- src/pages/dao/SafeController.tsx | 3 +- src/pages/home/SafeDisplayRow.tsx | 17 +---------- 5 files changed, 15 insertions(+), 59 deletions(-) diff --git a/src/components/ui/menus/SafesMenu/SafeMenuItem.tsx b/src/components/ui/menus/SafesMenu/SafeMenuItem.tsx index 76d5008051..2b3f9c1246 100644 --- a/src/components/ui/menus/SafesMenu/SafeMenuItem.tsx +++ b/src/components/ui/menus/SafesMenu/SafeMenuItem.tsx @@ -2,11 +2,9 @@ import { Box, Button, Flex, Image, MenuItem, Spacer, Text } from '@chakra-ui/rea import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { Address } from 'viem'; -import { useSwitchChain } from 'wagmi'; import { DAO_ROUTES } from '../../../../constants/routes'; import useAvatar from '../../../../hooks/utils/useAvatar'; -import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; -import { getChainIdFromPrefix, getNetworkIcon } from '../../../../utils/url'; +import { getNetworkIcon } from '../../../../utils/url'; import Avatar from '../../page/Header/Avatar'; export interface SafeMenuItemProps { @@ -20,26 +18,13 @@ export interface SafeMenuItemProps { export function SafeMenuItem({ address, network, name }: SafeMenuItemProps) { const navigate = useNavigate(); - const { addressPrefix } = useNetworkConfigStore(); - const { switchChain } = useSwitchChain({ - mutation: { - onSuccess: () => { - navigate(DAO_ROUTES.dao.relative(network, address)); - }, - }, - }); - // if by chance the safe name is an ENS name, let's attempt to get the avatar for that const avatarURL = useAvatar(name); const { t } = useTranslation('dashboard'); const onClickNav = () => { - if (addressPrefix !== network) { - switchChain({ chainId: getChainIdFromPrefix(network) }); - } else { - navigate(DAO_ROUTES.dao.relative(network, address)); - } + navigate(DAO_ROUTES.dao.relative(network, address)); }; return ( diff --git a/src/hooks/DAO/loaders/useFractalNode.ts b/src/hooks/DAO/loaders/useFractalNode.ts index d4c2a3e86e..863057f958 100644 --- a/src/hooks/DAO/loaders/useFractalNode.ts +++ b/src/hooks/DAO/loaders/useFractalNode.ts @@ -11,9 +11,11 @@ import { useDecentModules } from './useDecentModules'; export const useFractalNode = ({ addressPrefix, safeAddress, + wrongNetwork, }: { addressPrefix?: string; safeAddress?: Address; + wrongNetwork?: boolean; }) => { const safeApi = useSafeAPI(); const lookupModules = useDecentModules(); @@ -85,11 +87,11 @@ export const useFractalNode = ({ ]); useEffect(() => { - if (`${addressPrefix}${safeAddress}` !== currentValidSafe.current) { + if (`${addressPrefix}${safeAddress}` !== currentValidSafe.current && !wrongNetwork) { reset({ error: false }); setDAO(); } - }, [addressPrefix, safeAddress, setDAO, reset]); + }, [addressPrefix, safeAddress, setDAO, reset, wrongNetwork]); return { errorLoading }; }; diff --git a/src/hooks/utils/useAutomaticSwitchChain.ts b/src/hooks/utils/useAutomaticSwitchChain.ts index e738b67b8a..f226cda09b 100644 --- a/src/hooks/utils/useAutomaticSwitchChain.ts +++ b/src/hooks/utils/useAutomaticSwitchChain.ts @@ -1,35 +1,18 @@ import { useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { toast } from 'sonner'; -import { useSwitchChain } from 'wagmi'; import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { getChainIdFromPrefix } from '../../utils/url'; export const useAutomaticSwitchChain = ({ - addressPrefix, + urlAddressPrefix, }: { - addressPrefix: string | undefined; + urlAddressPrefix: string | undefined; }) => { - const { t } = useTranslation(['common']); - const networkConfig = useNetworkConfigStore(); - const { switchChain } = useSwitchChain({ - mutation: { - onSuccess: () => { - window.location.reload(); - }, - onError: error => { - if (error.name !== 'UserRejectedRequestError') { - toast.warning(t('automaticChainSwitchingErrorMessage')); - } - }, - }, - }); + const { setCurrentConfig, getConfigByChainId, addressPrefix } = useNetworkConfigStore(); useEffect(() => { - if (addressPrefix === undefined || networkConfig.addressPrefix === addressPrefix) { + if (urlAddressPrefix === undefined || addressPrefix === urlAddressPrefix) { return; } - - switchChain({ chainId: getChainIdFromPrefix(addressPrefix) }); - }, [switchChain, addressPrefix, networkConfig.addressPrefix]); + setCurrentConfig(getConfigByChainId(getChainIdFromPrefix(urlAddressPrefix))); + }, [addressPrefix, setCurrentConfig, getConfigByChainId, urlAddressPrefix]); }; diff --git a/src/pages/dao/SafeController.tsx b/src/pages/dao/SafeController.tsx index 2f3c92f688..9e57637001 100644 --- a/src/pages/dao/SafeController.tsx +++ b/src/pages/dao/SafeController.tsx @@ -20,17 +20,18 @@ import LoadingProblem from '../LoadingProblem'; export function SafeController() { const { invalidQuery, wrongNetwork, addressPrefix, safeAddress } = useParseSafeAddress(); + useAutomaticSwitchChain({ urlAddressPrefix: addressPrefix }); useUpdateSafeData(safeAddress); usePageTitle(); useTemporaryProposals(); - useAutomaticSwitchChain({ addressPrefix }); const { subgraphInfo } = useDaoInfoStore(); const { errorLoading } = useFractalNode({ addressPrefix, safeAddress, + wrongNetwork, }); useGovernanceContracts(); diff --git a/src/pages/home/SafeDisplayRow.tsx b/src/pages/home/SafeDisplayRow.tsx index 6f668f8d45..b3e1289a05 100644 --- a/src/pages/home/SafeDisplayRow.tsx +++ b/src/pages/home/SafeDisplayRow.tsx @@ -3,13 +3,11 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { Address } from 'viem'; -import { useSwitchChain } from 'wagmi'; import Avatar from '../../components/ui/page/Header/Avatar'; import { DAO_ROUTES } from '../../constants/routes'; import useAvatar from '../../hooks/utils/useAvatar'; import { createAccountSubstring } from '../../hooks/utils/useGetAccountName'; import { useGetSafeName } from '../../hooks/utils/useGetSafeName'; -import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { getChainIdFromPrefix, getChainName, getNetworkIcon } from '../../utils/url'; interface SafeDisplayRowProps { @@ -27,17 +25,8 @@ export function SafeDisplayRow({ showAddress, name, }: SafeDisplayRowProps) { - const { addressPrefix } = useNetworkConfigStore(); const navigate = useNavigate(); - const { switchChain } = useSwitchChain({ - mutation: { - onSuccess: () => { - navigate(DAO_ROUTES.dao.relative(network, address)); - }, - }, - }); - const { getSafeName } = useGetSafeName(getChainIdFromPrefix(network)); const [safeName, setSafeName] = useState(name); @@ -54,11 +43,7 @@ export function SafeDisplayRow({ const onClickNav = () => { if (onClick) onClick(); - if (addressPrefix !== network) { - switchChain({ chainId: getChainIdFromPrefix(network) }); - } else { - navigate(DAO_ROUTES.dao.relative(network, address)); - } + navigate(DAO_ROUTES.dao.relative(network, address)); }; const nameColor = showAddress ? 'neutral-7' : 'white-0'; From dc95f173734c9883cd6e541b5154845dcedc8a03 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:55:50 -0500 Subject: [PATCH 05/48] Refactor DaoHierarchyNode to remove unused chainId and streamline network configuration handling --- src/components/DaoHierarchy/DaoHierarchyNode.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/DaoHierarchy/DaoHierarchyNode.tsx b/src/components/DaoHierarchy/DaoHierarchyNode.tsx index 3d4e079bab..6b041fa226 100644 --- a/src/components/DaoHierarchy/DaoHierarchyNode.tsx +++ b/src/components/DaoHierarchy/DaoHierarchyNode.tsx @@ -6,7 +6,7 @@ import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Link as RouterLink } from 'react-router-dom'; import { Address, getContract, zeroAddress } from 'viem'; -import { useChainId, usePublicClient } from 'wagmi'; +import { usePublicClient } from 'wagmi'; import { DAOQueryDocument } from '../../../.graphclient'; import { SENTINEL_ADDRESS } from '../../constants/common'; import { DAO_ROUTES } from '../../constants/routes'; @@ -43,8 +43,7 @@ export function DaoHierarchyNode({ const safeApi = useSafeAPI(); const [hierarchyNode, setHierarchyNode] = useState(); const [hasErrorLoading, setErrorLoading] = useState(false); - const { addressPrefix, subgraph } = useNetworkConfigStore(); - const chainId = useChainId(); + const { addressPrefix, subgraph, chain } = useNetworkConfigStore(); const publicClient = usePublicClient(); const { getAddressContractType } = useAddressContractType(); @@ -168,7 +167,7 @@ export function DaoHierarchyNode({ if (safeAddress) { const cachedNode = getValue({ cacheName: CacheKeys.HIERARCHY_DAO_INFO, - chainId, + chainId: chain.id, daoAddress: safeAddress, }); if (cachedNode) { @@ -182,7 +181,7 @@ export function DaoHierarchyNode({ setValue( { cacheName: CacheKeys.HIERARCHY_DAO_INFO, - chainId, + chainId: chain.id, daoAddress: safeAddress, }, _node, From c8940273699abbc8d3d71a0583bee5874141b79d Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 6 Dec 2024 00:47:14 -0500 Subject: [PATCH 06/48] refactor: simplify WalletMenu component by removing containerRef prop --- src/components/ui/menus/AccountDisplay/WalletMenu.tsx | 6 +----- src/components/ui/menus/AccountDisplay/index.tsx | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/components/ui/menus/AccountDisplay/WalletMenu.tsx b/src/components/ui/menus/AccountDisplay/WalletMenu.tsx index 242767bbee..072615d26a 100644 --- a/src/components/ui/menus/AccountDisplay/WalletMenu.tsx +++ b/src/components/ui/menus/AccountDisplay/WalletMenu.tsx @@ -1,16 +1,14 @@ import { MenuList } from '@chakra-ui/react'; import { Link, Plugs } from '@phosphor-icons/react'; import { useWeb3Modal } from '@web3modal/wagmi/react'; -import { RefObject } from 'react'; import { useTranslation } from 'react-i18next'; import { useAccount, useDisconnect } from 'wagmi'; import { NEUTRAL_2_82_TRANSPARENT } from '../../../../constants/common'; import Divider from '../../utils/Divider'; import { ConnectedWalletMenuItem } from './ConnectedWalletMenuItem'; import { MenuItemButton } from './MenuItemButton'; -import { NetworkSelector } from './NetworkSelector'; -export function WalletMenu({ containerRef }: { containerRef: RefObject }) { +export function WalletMenu() { const user = useAccount(); const { disconnect } = useDisconnect(); const { open } = useWeb3Modal(); @@ -34,8 +32,6 @@ export function WalletMenu({ containerRef }: { containerRef: RefObject )} - - {!user.address && ( - + ); From 32e1ec56cf79f8d7e4e0708e7b63085bb3e13e46 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 6 Dec 2024 01:28:05 -0500 Subject: [PATCH 07/48] refactor: update network config hook usage to useNetworkConfigStore --- src/components/Roles/RoleDetails.tsx | 4 ++-- src/pages/dao/treasury/SafeTreasuryPage.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Roles/RoleDetails.tsx b/src/components/Roles/RoleDetails.tsx index d0e88589ac..ed4fef5786 100644 --- a/src/components/Roles/RoleDetails.tsx +++ b/src/components/Roles/RoleDetails.tsx @@ -10,7 +10,7 @@ import useAvatar from '../../hooks/utils/useAvatar'; import { useCanUserCreateProposal } from '../../hooks/utils/useCanUserSubmitProposal'; import { useCopyText } from '../../hooks/utils/useCopyText'; import { useGetAccountName } from '../../hooks/utils/useGetAccountName'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore'; import { paymentSorterByActiveStatus, @@ -79,7 +79,7 @@ export default function RolesDetails({ }) { const { safe } = useDaoInfoStore(); const navigate = useNavigate(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const permissionsContainerRef = useRef(null); const roleHatWearer = 'wearer' in roleHat ? roleHat.wearer : roleHat.wearerAddress; diff --git a/src/pages/dao/treasury/SafeTreasuryPage.tsx b/src/pages/dao/treasury/SafeTreasuryPage.tsx index 5a97332519..bbad65aeb7 100644 --- a/src/pages/dao/treasury/SafeTreasuryPage.tsx +++ b/src/pages/dao/treasury/SafeTreasuryPage.tsx @@ -17,7 +17,7 @@ import { DAO_ROUTES } from '../../../constants/routes'; import { useCanUserCreateProposal } from '../../../hooks/utils/useCanUserSubmitProposal'; import { analyticsEvents } from '../../../insights/analyticsEvents'; import { useFractal } from '../../../providers/App/AppProvider'; -import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; +import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useProposalActionsStore } from '../../../store/actions/useProposalActionsStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { ProposalActionType } from '../../../types'; @@ -41,7 +41,7 @@ export function SafeTreasuryPage() { const { isOpen, onOpen, onClose } = useDisclosure(); const { addAction } = useProposalActionsStore(); const navigate = useNavigate(); - const { addressPrefix } = useNetworkConfig(); + const { addressPrefix } = useNetworkConfigStore(); const hasAnyBalanceOfAnyFungibleTokens = assetsFungible.reduce((p, c) => p + BigInt(c.balance), 0n) > 0n; From 047d978956a76a4b8c03f16159b1ad412ac53410 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 6 Dec 2024 01:47:45 -0500 Subject: [PATCH 08/48] refactor: reorganize DAOInfoCard layout by grouping FavoriteIcon and ManageDAOMenu --- src/components/ui/cards/DAOInfoCard.tsx | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/components/ui/cards/DAOInfoCard.tsx b/src/components/ui/cards/DAOInfoCard.tsx index 7b7a84379a..40e4d7964e 100644 --- a/src/components/ui/cards/DAOInfoCard.tsx +++ b/src/components/ui/cards/DAOInfoCard.tsx @@ -48,19 +48,12 @@ export function DAOInfoCard() { alignItems="center" columnGap="0.5rem" justifyContent="space-between" - mt="0.5rem" flexGrow={1} > - {/* FAVORITE ICON */} - toggleFavorite(displayedAddress, daoName)} - /> - {/* PARENT TAG */} {!!node.subgraphInfo && node.subgraphInfo.childAddresses.length > 0 && ( )} - {/* SETTINGS MENU BUTTON */} - + + {/* FAVORITE ICON */} + toggleFavorite(displayedAddress, daoName)} + /> + {/* SETTINGS MENU BUTTON */} + + {/* DAO NAME AND ACTIONS */} From 2e3b524fdc66d62886f054cff893bd3f658a22f2 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 6 Dec 2024 01:53:53 -0500 Subject: [PATCH 09/48] refactor: add network icon to DAOInfoCard component --- src/components/ui/cards/DAOInfoCard.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/ui/cards/DAOInfoCard.tsx b/src/components/ui/cards/DAOInfoCard.tsx index 40e4d7964e..88f74ee1e4 100644 --- a/src/components/ui/cards/DAOInfoCard.tsx +++ b/src/components/ui/cards/DAOInfoCard.tsx @@ -1,10 +1,11 @@ -import { Box, Center, Flex, Link, Text } from '@chakra-ui/react'; +import { Box, Center, Flex, Image, Link, Text } from '@chakra-ui/react'; import { Link as RouterLink } from 'react-router-dom'; import { DAO_ROUTES } from '../../../constants/routes'; import { useAccountFavorites } from '../../../hooks/DAO/loaders/useFavorites'; import { useGetAccountName } from '../../../hooks/utils/useGetAccountName'; import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; +import { getNetworkIcon } from '../../../utils/url'; import { SnapshotButton } from '../badges/Snapshot'; import { FavoriteIcon } from '../icons/FavoriteIcon'; import AddressCopier from '../links/AddressCopier'; @@ -54,6 +55,7 @@ export function DAOInfoCard() { alignItems="center" columnGap="0.5rem" > + {/* PARENT TAG */} {!!node.subgraphInfo && node.subgraphInfo.childAddresses.length > 0 && ( Date: Fri, 6 Dec 2024 02:33:07 -0500 Subject: [PATCH 10/48] refactor: rename NetworkSelector component and update import paths --- .../ui/{menus/AccountDisplay => utils}/NetworkSelector.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename src/components/ui/{menus/AccountDisplay => utils}/NetworkSelector.tsx (94%) diff --git a/src/components/ui/menus/AccountDisplay/NetworkSelector.tsx b/src/components/ui/utils/NetworkSelector.tsx similarity index 94% rename from src/components/ui/menus/AccountDisplay/NetworkSelector.tsx rename to src/components/ui/utils/NetworkSelector.tsx index 3d9cae3a08..a2a2e49c0c 100644 --- a/src/components/ui/menus/AccountDisplay/NetworkSelector.tsx +++ b/src/components/ui/utils/NetworkSelector.tsx @@ -4,10 +4,10 @@ import { RefObject } from 'react'; import { useTranslation } from 'react-i18next'; import { useSwitchChain } from 'wagmi'; import { - supportedNetworks, useNetworkConfigStore, -} from '../../../../providers/NetworkConfig/useNetworkConfigStore'; -import { OptionMenu } from '../OptionMenu'; + supportedNetworks, +} from '../../../providers/NetworkConfig/useNetworkConfigStore'; +import { OptionMenu } from '../menus/OptionMenu'; /** * Network display for menu From 1845811ef6d1ead32ff9ef7043235b3d4e04cfc6 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:52:03 -0500 Subject: [PATCH 11/48] feat: add DropdownMenu component and remove obsolete NetworkSelector --- src/components/ui/menus/DropdownMenu.tsx | 306 ++++++++++++++++++++ src/components/ui/utils/NetworkSelector.tsx | 80 ----- 2 files changed, 306 insertions(+), 80 deletions(-) create mode 100644 src/components/ui/menus/DropdownMenu.tsx delete mode 100644 src/components/ui/utils/NetworkSelector.tsx diff --git a/src/components/ui/menus/DropdownMenu.tsx b/src/components/ui/menus/DropdownMenu.tsx new file mode 100644 index 0000000000..cd310f3146 --- /dev/null +++ b/src/components/ui/menus/DropdownMenu.tsx @@ -0,0 +1,306 @@ +import { + Button, + Divider, + Flex, + Icon, + Image, + Menu, + MenuButton, + MenuItem, + MenuList, + Show, + Text, +} from '@chakra-ui/react'; +import { CaretDown, CheckCircle } from '@phosphor-icons/react'; +import { CARD_SHADOW } from '../../../constants/common'; +import DraggableDrawer from '../containers/DraggableDrawer'; +import { EaseOutComponent } from '../utils/EaseOutComponent'; +type DropdownItem = T & { + value: string; + label: string; + icon?: string; + selected?: boolean; +}; +interface DropdownMenuProps { + items: DropdownItem[]; + selectedItem?: DropdownItem; + title?: string; + onSelect: (item: DropdownItem) => void; + isDisabled?: boolean; + selectPlaceholder?: string; + emptyMessage?: string; + /** + * Optional custom renderer for each item. + * If provided, the returned node should include everything inside the MenuItem, + * including the selected icon if desired. + */ + renderItem?: (item: DropdownItem, isSelected: boolean) => React.ReactNode; +} + +export function DropdownMenu({ + items, + selectedItem, + title, + onSelect, + isDisabled, + selectPlaceholder = 'Select', + emptyMessage = 'No items available', + renderItem, +}: DropdownMenuProps) { + return ( + + {({ isOpen, onClose }) => ( + <> + + + {selectedItem?.icon && ( + + )} + + + {selectedItem?.label ?? selectPlaceholder} + + + + + + + {/* Mobile view: Draggable Drawer */} + + {}} + onClose={onClose} + closeOnOverlayClick + headerContent={ + + {title && {title}} + + + } + > + + {items.length === 0 && ( + + + {emptyMessage} + + + )} + {items.map((item, index) => { + const isSelected = !!item.selected; + return ( + { + onSelect(item); + onClose(); + }} + > + {renderItem ? ( + renderItem(item, isSelected) + ) : ( + <> + + {item.icon && ( + + )} + + {item.label} + + + {isSelected && ( + + )} + + )} + + ); + })} + + + + + {/* Desktop view: MenuList */} + + + + {title && ( + <> + + {title} + + + + )} + {items.length === 0 && ( + + + {emptyMessage} + + + )} + {items.map((item, index) => { + const isSelected = !!item.selected; + return ( + onSelect(item)} + > + {renderItem ? ( + renderItem(item, isSelected) + ) : ( + <> + + {item.icon && ( + + )} + + {item.label} + + + {isSelected && ( + + )} + + )} + + ); + })} + + + + + )} + + ); +} diff --git a/src/components/ui/utils/NetworkSelector.tsx b/src/components/ui/utils/NetworkSelector.tsx deleted file mode 100644 index a2a2e49c0c..0000000000 --- a/src/components/ui/utils/NetworkSelector.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { Box, Flex, Icon, Text } from '@chakra-ui/react'; -import { CaretDown } from '@phosphor-icons/react'; -import { RefObject } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSwitchChain } from 'wagmi'; -import { - useNetworkConfigStore, - supportedNetworks, -} from '../../../providers/NetworkConfig/useNetworkConfigStore'; -import { OptionMenu } from '../menus/OptionMenu'; - -/** - * Network display for menu - */ -export function NetworkSelector({ - containerRef, -}: { - containerRef: RefObject; -}) { - const { t } = useTranslation('menu'); - const { chain } = useNetworkConfigStore(); - const { switchChain } = useSwitchChain(); - const networksOptions = supportedNetworks.map(network => ({ - optionKey: network.chain.name, - onClick: () => switchChain({ chainId: network.chain.id }), - })); - - return ( - - - - {t('network')} - - - - {chain.name} - - - } - /> - - - - ); -} From d226d81e81e8cbf8497b831eebfdb4db9bc873a2 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:52:13 -0500 Subject: [PATCH 12/48] refactor: streamline DAOInfoCard component by enhancing layout and improving icon visibility --- .../Roles/forms/RoleFormAssetSelector.tsx | 345 ++++++------------ 1 file changed, 108 insertions(+), 237 deletions(-) diff --git a/src/components/Roles/forms/RoleFormAssetSelector.tsx b/src/components/Roles/forms/RoleFormAssetSelector.tsx index 0f7e2db665..a283537c0b 100644 --- a/src/components/Roles/forms/RoleFormAssetSelector.tsx +++ b/src/components/Roles/forms/RoleFormAssetSelector.tsx @@ -1,18 +1,5 @@ -import { - Button, - Divider, - Flex, - FormControl, - Icon, - Image, - Menu, - MenuButton, - MenuItem, - MenuList, - Show, - Text, -} from '@chakra-ui/react'; -import { CaretDown, CheckCircle } from '@phosphor-icons/react'; +import { Flex, FormControl, Image, Text, Icon } from '@chakra-ui/react'; +import { CheckCircle } from '@phosphor-icons/react'; import { Field, FieldInputProps, @@ -23,136 +10,44 @@ import { } from 'formik'; import { useTranslation } from 'react-i18next'; import { getAddress } from 'viem'; -import { CARD_SHADOW } from '../../../constants/common'; import { useFractal } from '../../../providers/App/AppProvider'; import { BigIntValuePair } from '../../../types'; import { RoleFormValues } from '../../../types/roles'; import { formatCoin, formatUSD } from '../../../utils'; import { MOCK_MORALIS_ETH_ADDRESS } from '../../../utils/address'; -import DraggableDrawer from '../../ui/containers/DraggableDrawer'; import { BigIntInput } from '../../ui/forms/BigIntInput'; import LabelWrapper from '../../ui/forms/LabelWrapper'; -import { EaseOutComponent } from '../../ui/utils/EaseOutComponent'; +import { DropdownMenu } from '../../ui/menus/DropdownMenu'; -function AssetsList({ formIndex }: { formIndex: number }) { - const { t } = useTranslation('roles'); +export function AssetSelector({ formIndex, disabled }: { formIndex: number; disabled?: boolean }) { + const { t } = useTranslation(['roles', 'treasury', 'modals']); + const { values, setFieldValue } = useFormikContext(); const { treasury: { assetsFungible }, } = useFractal(); + const fungibleAssetsWithBalance = assetsFungible.filter( asset => parseFloat(asset.balance) > 0 && - asset.tokenAddress.toLowerCase() !== MOCK_MORALIS_ETH_ADDRESS.toLowerCase(), // Can't stream native token + asset.tokenAddress.toLowerCase() !== MOCK_MORALIS_ETH_ADDRESS.toLowerCase(), ); - const { values, setFieldValue } = useFormikContext(); - const selectedAsset = values.roleEditing?.payments?.[formIndex]?.asset; - if (fungibleAssetsWithBalance.length === 0) { - return ( - - - {t('emptyRolesAssets')} - - - ); - } - - return ( - <> - {fungibleAssetsWithBalance.map((asset, index) => { - const isSelected = selectedAsset?.address === asset.tokenAddress; - return ( - { - setFieldValue(`roleEditing.payments.${formIndex}.asset`, { - name: asset.name, - address: getAddress(asset.tokenAddress), - symbol: asset.symbol, - logo: asset.logo ?? '', - decimals: asset.decimals, - }); - }} - > - - - - - {asset?.symbol} - - - - {formatCoin(asset?.balance, true, asset?.decimals, asset?.symbol, true)} - - {asset?.usdValue && ( - <> - - {'•'} - - - {formatUSD(asset?.usdValue)} - - - )} - - - - {isSelected && ( - - )} - - ); - })} - - ); -} - -export function AssetSelector({ formIndex, disabled }: { formIndex: number; disabled?: boolean }) { - const { t } = useTranslation(['roles', 'treasury', 'modals']); - const { values, setFieldValue } = useFormikContext(); const selectedAsset = values.roleEditing?.payments?.[formIndex]?.asset; + const dropdownItems = fungibleAssetsWithBalance.map(asset => ({ + value: asset.tokenAddress, + label: asset.symbol, + icon: asset.logo ?? asset.thumbnail ?? '/images/coin-icon-default.svg', + selected: selectedAsset?.address === getAddress(asset.tokenAddress), + assetData: { + name: asset.name, + balance: asset.balance, + decimals: asset.decimals, + usdValue: asset.usdValue, + symbol: asset.symbol, + }, + })); + return ( <> {() => ( - - {({ isOpen, onClose }) => ( - <> - + + items={dropdownItems} + selectedItem={dropdownItems.find(item => item.selected)} + onSelect={item => { + const chosenAsset = fungibleAssetsWithBalance.find( + asset => getAddress(asset.tokenAddress) === getAddress(item.value), + ); + if (chosenAsset) { + setFieldValue(`roleEditing.payments.${formIndex}.asset`, { + name: chosenAsset.name, + address: getAddress(chosenAsset.tokenAddress), + symbol: chosenAsset.symbol, + logo: chosenAsset.logo ?? '', + decimals: chosenAsset.decimals, + }); + } else { + setFieldValue(`roleEditing.payments.${formIndex}.asset`, undefined); + } + }} + title={t('titleAssets', { ns: 'treasury' })} + isDisabled={disabled} + selectPlaceholder={t('selectLabel', { ns: 'modals' })} + emptyMessage={t('emptyRolesAssets', { ns: 'roles' })} + renderItem={(item, isSelected) => { + const { balance, decimals, usdValue, symbol } = item.assetData; + const balanceText = formatCoin(balance, true, decimals, symbol, true); + + return ( + <> - - + + - {selectedAsset?.symbol ?? t('selectLabel', { ns: 'modals' })} + {item.label} - - - - - - {}} - onClose={onClose} - closeOnOverlayClick - headerContent={ - - {t('titleAssets', { ns: 'treasury' })} + + {balanceText} - + {usdValue && ( + <> + + {'•'} + + + {formatUSD(usdValue)} + + + )} - } - > - - - - - - - - - {t('titleAssets', { ns: 'treasury' })} - - - - - - - - )} - + + {isSelected && ( + + )} + + ); + }} + /> )} @@ -299,13 +169,14 @@ export function AssetSelector({ formIndex, disabled }: { formIndex: number; disa const paymentAmountBigIntError = meta.error as FormikErrors; const paymentAmountBigIntTouched = meta.touched; const inputDisabled = !values?.roleEditing?.payments?.[formIndex]?.asset || disabled; + return ( @@ -316,11 +187,11 @@ export function AssetSelector({ formIndex, disabled }: { formIndex: number; disa onChange={valuePair => { setFieldValue(`roleEditing.payments.${formIndex}.amount`, valuePair, true); }} - decimalPlaces={values?.roleEditing?.payments?.[formIndex]?.asset?.decimals} + decimalPlaces={selectedAsset?.decimals} onBlur={() => { setFieldTouched(`roleEditing.payments.${formIndex}.amount`, true); }} - cursor={disabled ? 'not-allowed' : 'pointer'} + cursor={inputDisabled ? 'not-allowed' : 'pointer'} placeholder="0" /> From 0ff3f84eafe18b2ddfba6b1aeadfdae851507f18 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:52:19 -0500 Subject: [PATCH 13/48] refactor: update network configuration to use mainnet instead of sepolia --- src/providers/NetworkConfig/useNetworkConfigStore.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/providers/NetworkConfig/useNetworkConfigStore.ts b/src/providers/NetworkConfig/useNetworkConfigStore.ts index ad2950db82..46d4cb2249 100644 --- a/src/providers/NetworkConfig/useNetworkConfigStore.ts +++ b/src/providers/NetworkConfig/useNetworkConfigStore.ts @@ -1,7 +1,7 @@ import { create } from 'zustand'; import { NetworkConfig } from '../../types/network'; import { networks } from './networks'; -import { sepoliaConfig } from './networks/sepolia'; +import { mainnetConfig } from './networks/mainnet'; interface NetworkConfigStore extends NetworkConfig { getConfigByChainId: (chainId?: number) => NetworkConfig; @@ -25,7 +25,7 @@ export const getNetworkConfig = (chainId?: number): NetworkConfig => { // Create the Zustand store export const useNetworkConfigStore = create(set => ({ - ...sepoliaConfig, + ...mainnetConfig, getConfigByChainId: getNetworkConfig, setCurrentConfig: (config: NetworkConfig) => set({ ...config }), })); From 736377cb1e7d3b86dde689ec4d64d1500a5c43b1 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:53:10 -0500 Subject: [PATCH 14/48] feat: implement DropdownMenu for network selection in EstablishEssentials component --- .../formComponents/EstablishEssentials.tsx | 63 ++++++++++++++++++- src/i18n/locales/en/daoCreate.json | 4 +- src/i18n/locales/en/menu.json | 1 - 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/src/components/DaoCreator/formComponents/EstablishEssentials.tsx b/src/components/DaoCreator/formComponents/EstablishEssentials.tsx index 3f69f98d03..ccab7d363c 100644 --- a/src/components/DaoCreator/formComponents/EstablishEssentials.tsx +++ b/src/components/DaoCreator/formComponents/EstablishEssentials.tsx @@ -1,14 +1,20 @@ -import { Box, Input, RadioGroup } from '@chakra-ui/react'; +import { Box, Flex, Icon, Image, Input, RadioGroup } from '@chakra-ui/react'; +import { CheckCircle } from '@phosphor-icons/react'; import debounce from 'lodash.debounce'; import { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { createAccountSubstring } from '../../../hooks/utils/useGetAccountName'; -import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; +import { + supportedNetworks, + useNetworkConfigStore, +} from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { GovernanceType, ICreationStepProps, VotingStrategyType } from '../../../types'; +import { getNetworkIcon } from '../../../utils/url'; import { InputComponent, LabelComponent } from '../../ui/forms/InputComponent'; import LabelWrapper from '../../ui/forms/LabelWrapper'; import { RadioWithText } from '../../ui/forms/Radio/RadioWithText'; +import { DropdownMenu } from '../../ui/menus/DropdownMenu'; import { StepButtons } from '../StepButtons'; import { StepWrapper } from '../StepWrapper'; @@ -65,7 +71,7 @@ export function EstablishEssentials(props: ICreationStepProps) { setFieldValue('essentials.governance', value); }; - const { createOptions } = useNetworkConfigStore(); + const { createOptions, setCurrentConfig, chain, getConfigByChainId } = useNetworkConfigStore(); const [snapshotENSInput, setSnapshotENSInput] = useState(''); @@ -81,6 +87,13 @@ export function EstablishEssentials(props: ICreationStepProps) { debounceENSInput(snapshotENSInput); }, [debounceENSInput, snapshotENSInput]); + const dropdownItems = supportedNetworks.map(network => ({ + value: network.chain.id.toString(), + label: network.chain.name, + icon: getNetworkIcon(network.addressPrefix), + selected: chain.id === network.chain.id, + })); + return ( <> + + + item.selected)} + onSelect={item => { + setCurrentConfig(getConfigByChainId(Number(item.value))); + }} + title={t('networks')} + isDisabled={false} + renderItem={(item, isSelected) => { + return ( + <> + + + {item.label} + + {isSelected && ( + + )} + + ); + }} + /> + + Date: Fri, 6 Dec 2024 15:02:48 -0500 Subject: [PATCH 15/48] refactor: adjust label alignment in EstablishEssentials component --- src/components/DaoCreator/formComponents/EstablishEssentials.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/DaoCreator/formComponents/EstablishEssentials.tsx b/src/components/DaoCreator/formComponents/EstablishEssentials.tsx index ccab7d363c..1db69f86ea 100644 --- a/src/components/DaoCreator/formComponents/EstablishEssentials.tsx +++ b/src/components/DaoCreator/formComponents/EstablishEssentials.tsx @@ -123,6 +123,7 @@ export function EstablishEssentials(props: ICreationStepProps) { label={t('networks')} helper={t('networkDescription')} isRequired + alignLabel="flex-start" > Date: Fri, 6 Dec 2024 15:03:11 -0500 Subject: [PATCH 16/48] refactor: adjust icon size and text style in DropdownMenu component --- src/components/ui/menus/DropdownMenu.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ui/menus/DropdownMenu.tsx b/src/components/ui/menus/DropdownMenu.tsx index cd310f3146..9694f412f1 100644 --- a/src/components/ui/menus/DropdownMenu.tsx +++ b/src/components/ui/menus/DropdownMenu.tsx @@ -84,7 +84,7 @@ export function DropdownMenu({ )} ({ gap="0.75rem" > {selectedItem?.label ?? selectPlaceholder} From 56b0eda552b42a39bb1afc1996e2d8837e2e324d Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 6 Dec 2024 16:36:15 -0500 Subject: [PATCH 17/48] feat: implement dynamic Wagmi configuration and update providers to use it --- src/components/ui/page/Global/index.tsx | 6 ++--- .../NetworkConfig/useDynamicWagmiConfig.ts | 27 +++++++++++++++++++ .../NetworkConfig/web3-modal.config.ts | 27 ++++--------------- src/providers/Providers.tsx | 6 +++-- 4 files changed, 39 insertions(+), 27 deletions(-) create mode 100644 src/providers/NetworkConfig/useDynamicWagmiConfig.ts diff --git a/src/components/ui/page/Global/index.tsx b/src/components/ui/page/Global/index.tsx index 3a2e3df2ea..65e226c9a1 100644 --- a/src/components/ui/page/Global/index.tsx +++ b/src/components/ui/page/Global/index.tsx @@ -11,17 +11,16 @@ import { } from '../../../../hooks/utils/cache/cacheDefaults'; import { setValue } from '../../../../hooks/utils/cache/useLocalStorage'; import { getSafeName } from '../../../../hooks/utils/useGetSafeName'; +import { useDynamicWagmiConfig } from '../../../../providers/NetworkConfig/useDynamicWagmiConfig'; import { getNetworkConfig, supportedNetworks, } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; -import { wagmiConfig } from '../../../../providers/NetworkConfig/web3-modal.config'; import { getChainIdFromPrefix } from '../../../../utils/url'; import { Layout } from '../Layout'; const useUserTracking = () => { const { address } = useAccount(); - useEffect(() => { Sentry.setUser(address ? { id: address } : null); if (address) { @@ -34,6 +33,7 @@ const useUserTracking = () => { const useUpdateFavoritesCache = (onFavoritesUpdated: () => void) => { const { favoritesList } = useAccountFavorites(); + const wagmiConfig = useDynamicWagmiConfig(); useEffect(() => { (async () => { @@ -102,7 +102,7 @@ const useUpdateFavoritesCache = (onFavoritesUpdated: () => void) => { onFavoritesUpdated(); } })(); - }, [favoritesList, onFavoritesUpdated]); + }, [favoritesList, onFavoritesUpdated, wagmiConfig.chains]); }; export function Global() { diff --git a/src/providers/NetworkConfig/useDynamicWagmiConfig.ts b/src/providers/NetworkConfig/useDynamicWagmiConfig.ts new file mode 100644 index 0000000000..72a73c05f4 --- /dev/null +++ b/src/providers/NetworkConfig/useDynamicWagmiConfig.ts @@ -0,0 +1,27 @@ +import { createWeb3Modal } from '@web3modal/wagmi/react'; +import { defaultWagmiConfig } from '@web3modal/wagmi/react/config'; +import { supportedNetworks, useNetworkConfigStore } from './useNetworkConfigStore'; +import { transportsReducer, wagmiMetadata, walletConnectProjectId } from './web3-modal.config'; + +// Dynamic Wagmi Config Hook +export const useDynamicWagmiConfig = () => { + const { chain } = useNetworkConfigStore(); + + const wagmiConfig = defaultWagmiConfig({ + chains: [chain], + projectId: walletConnectProjectId, + metadata: wagmiMetadata, + transports: supportedNetworks.reduce(transportsReducer, {}), + batch: { + multicall: true, + }, + }); + + // Initialize Web3Modal only once + if (walletConnectProjectId) { + createWeb3Modal({ wagmiConfig, projectId: walletConnectProjectId }); + } + + // Return the dynamic Wagmi client + return wagmiConfig; +}; diff --git a/src/providers/NetworkConfig/web3-modal.config.ts b/src/providers/NetworkConfig/web3-modal.config.ts index f721c48b14..1fed495d75 100644 --- a/src/providers/NetworkConfig/web3-modal.config.ts +++ b/src/providers/NetworkConfig/web3-modal.config.ts @@ -1,18 +1,12 @@ import { QueryClient } from '@tanstack/react-query'; -import { createWeb3Modal } from '@web3modal/wagmi/react'; -import { defaultWagmiConfig } from '@web3modal/wagmi/react/config'; import { HttpTransport } from 'viem'; import { http } from 'wagmi'; -import { Chain } from 'wagmi/chains'; import { NetworkConfig } from '../../types/network'; -import { supportedNetworks } from './useNetworkConfigStore'; - -const supportedWagmiChains = supportedNetworks.map(network => network.chain); export const walletConnectProjectId = import.meta.env.VITE_APP_WALLET_CONNECT_PROJECT_ID; export const queryClient = new QueryClient(); -const wagmiMetadata = { +export const wagmiMetadata = { name: import.meta.env.VITE_APP_NAME, description: 'Are you outgrowing your Multisig? Decent extends Safe treasuries into on-chain hierarchies of permissions, token flows, and governance.', @@ -20,21 +14,10 @@ const wagmiMetadata = { icons: [`${import.meta.env.VITE_APP_SITE_URL}/favicon-96x96.png`], }; -const transportsReducer = (accumulator: Record, network: NetworkConfig) => { +export const transportsReducer = ( + accumulator: Record, + network: NetworkConfig, +) => { accumulator[network.chain.id] = http(network.rpcEndpoint); return accumulator; }; - -export const wagmiConfig = defaultWagmiConfig({ - chains: supportedWagmiChains as [Chain, ...Chain[]], - projectId: walletConnectProjectId, - metadata: wagmiMetadata, - transports: supportedNetworks.reduce(transportsReducer, {}), - batch: { - multicall: true, - }, -}); - -if (walletConnectProjectId) { - createWeb3Modal({ wagmiConfig, projectId: walletConnectProjectId }); -} diff --git a/src/providers/Providers.tsx b/src/providers/Providers.tsx index dfb14b094d..7568b4d33d 100644 --- a/src/providers/Providers.tsx +++ b/src/providers/Providers.tsx @@ -9,9 +9,11 @@ import { ErrorBoundary } from '../components/ui/utils/ErrorBoundary'; import { TopErrorFallback } from '../components/ui/utils/TopErrorFallback'; import graphQLClient from '../graphql'; import { AppProvider } from './App/AppProvider'; -import { queryClient, wagmiConfig } from './NetworkConfig/web3-modal.config'; +import { useDynamicWagmiConfig } from './NetworkConfig/useDynamicWagmiConfig'; +import { queryClient } from './NetworkConfig/web3-modal.config'; export default function Providers({ children }: { children: ReactNode }) { + const currentConfig = useDynamicWagmiConfig(); return ( - + Date: Sat, 7 Dec 2024 17:32:43 -0500 Subject: [PATCH 18/48] refactor: remove unused dynamic Wagmi configuration hook --- src/components/ui/page/Global/index.tsx | 5 ++-- .../NetworkConfig/useDynamicWagmiConfig.ts | 27 ------------------- src/providers/Providers.tsx | 6 ++--- 3 files changed, 4 insertions(+), 34 deletions(-) delete mode 100644 src/providers/NetworkConfig/useDynamicWagmiConfig.ts diff --git a/src/components/ui/page/Global/index.tsx b/src/components/ui/page/Global/index.tsx index 65e226c9a1..21e1be827a 100644 --- a/src/components/ui/page/Global/index.tsx +++ b/src/components/ui/page/Global/index.tsx @@ -11,11 +11,11 @@ import { } from '../../../../hooks/utils/cache/cacheDefaults'; import { setValue } from '../../../../hooks/utils/cache/useLocalStorage'; import { getSafeName } from '../../../../hooks/utils/useGetSafeName'; -import { useDynamicWagmiConfig } from '../../../../providers/NetworkConfig/useDynamicWagmiConfig'; import { getNetworkConfig, supportedNetworks, } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; +import { wagmiConfig } from '../../../../providers/NetworkConfig/web3-modal.config'; import { getChainIdFromPrefix } from '../../../../utils/url'; import { Layout } from '../Layout'; @@ -33,7 +33,6 @@ const useUserTracking = () => { const useUpdateFavoritesCache = (onFavoritesUpdated: () => void) => { const { favoritesList } = useAccountFavorites(); - const wagmiConfig = useDynamicWagmiConfig(); useEffect(() => { (async () => { @@ -102,7 +101,7 @@ const useUpdateFavoritesCache = (onFavoritesUpdated: () => void) => { onFavoritesUpdated(); } })(); - }, [favoritesList, onFavoritesUpdated, wagmiConfig.chains]); + }, [favoritesList, onFavoritesUpdated]); }; export function Global() { diff --git a/src/providers/NetworkConfig/useDynamicWagmiConfig.ts b/src/providers/NetworkConfig/useDynamicWagmiConfig.ts deleted file mode 100644 index 72a73c05f4..0000000000 --- a/src/providers/NetworkConfig/useDynamicWagmiConfig.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { createWeb3Modal } from '@web3modal/wagmi/react'; -import { defaultWagmiConfig } from '@web3modal/wagmi/react/config'; -import { supportedNetworks, useNetworkConfigStore } from './useNetworkConfigStore'; -import { transportsReducer, wagmiMetadata, walletConnectProjectId } from './web3-modal.config'; - -// Dynamic Wagmi Config Hook -export const useDynamicWagmiConfig = () => { - const { chain } = useNetworkConfigStore(); - - const wagmiConfig = defaultWagmiConfig({ - chains: [chain], - projectId: walletConnectProjectId, - metadata: wagmiMetadata, - transports: supportedNetworks.reduce(transportsReducer, {}), - batch: { - multicall: true, - }, - }); - - // Initialize Web3Modal only once - if (walletConnectProjectId) { - createWeb3Modal({ wagmiConfig, projectId: walletConnectProjectId }); - } - - // Return the dynamic Wagmi client - return wagmiConfig; -}; diff --git a/src/providers/Providers.tsx b/src/providers/Providers.tsx index 7568b4d33d..dfb14b094d 100644 --- a/src/providers/Providers.tsx +++ b/src/providers/Providers.tsx @@ -9,11 +9,9 @@ import { ErrorBoundary } from '../components/ui/utils/ErrorBoundary'; import { TopErrorFallback } from '../components/ui/utils/TopErrorFallback'; import graphQLClient from '../graphql'; import { AppProvider } from './App/AppProvider'; -import { useDynamicWagmiConfig } from './NetworkConfig/useDynamicWagmiConfig'; -import { queryClient } from './NetworkConfig/web3-modal.config'; +import { queryClient, wagmiConfig } from './NetworkConfig/web3-modal.config'; export default function Providers({ children }: { children: ReactNode }) { - const currentConfig = useDynamicWagmiConfig(); return ( - + Date: Sat, 7 Dec 2024 17:33:31 -0500 Subject: [PATCH 19/48] feat: integrate automatic chain switching and update Wagmi configuration for network support --- src/hooks/utils/useAutomaticSwitchChain.ts | 19 ++++++++++++++-- .../NetworkConfig/web3-modal.config.ts | 22 ++++++++++++++++++- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/hooks/utils/useAutomaticSwitchChain.ts b/src/hooks/utils/useAutomaticSwitchChain.ts index f226cda09b..2459cc18ee 100644 --- a/src/hooks/utils/useAutomaticSwitchChain.ts +++ b/src/hooks/utils/useAutomaticSwitchChain.ts @@ -1,4 +1,5 @@ import { useEffect } from 'react'; +import { useSwitchChain } from 'wagmi'; import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { getChainIdFromPrefix } from '../../utils/url'; @@ -8,11 +9,25 @@ export const useAutomaticSwitchChain = ({ urlAddressPrefix: string | undefined; }) => { const { setCurrentConfig, getConfigByChainId, addressPrefix } = useNetworkConfigStore(); + const { switchChain } = useSwitchChain({ + mutation: { + onError: () => { + if (addressPrefix !== urlAddressPrefix && urlAddressPrefix !== undefined) { + const chainId = getChainIdFromPrefix(urlAddressPrefix); + switchChain({ chainId }); + } + }, + }, + }); useEffect(() => { if (urlAddressPrefix === undefined || addressPrefix === urlAddressPrefix) { return; } - setCurrentConfig(getConfigByChainId(getChainIdFromPrefix(urlAddressPrefix))); - }, [addressPrefix, setCurrentConfig, getConfigByChainId, urlAddressPrefix]); + const chainId = getChainIdFromPrefix(urlAddressPrefix); + setCurrentConfig(getConfigByChainId(chainId)); + if (addressPrefix !== urlAddressPrefix && urlAddressPrefix !== undefined) { + switchChain({ chainId }); + } + }, [addressPrefix, setCurrentConfig, getConfigByChainId, urlAddressPrefix, switchChain]); }; diff --git a/src/providers/NetworkConfig/web3-modal.config.ts b/src/providers/NetworkConfig/web3-modal.config.ts index 1fed495d75..2d9e4e2d61 100644 --- a/src/providers/NetworkConfig/web3-modal.config.ts +++ b/src/providers/NetworkConfig/web3-modal.config.ts @@ -1,12 +1,18 @@ import { QueryClient } from '@tanstack/react-query'; +import { createWeb3Modal } from '@web3modal/wagmi/react'; +import { defaultWagmiConfig } from '@web3modal/wagmi/react/config'; import { HttpTransport } from 'viem'; import { http } from 'wagmi'; +import { Chain } from 'wagmi/chains'; import { NetworkConfig } from '../../types/network'; +import { supportedNetworks } from './useNetworkConfigStore'; + +const supportedWagmiChains = supportedNetworks.map(network => network.chain); export const walletConnectProjectId = import.meta.env.VITE_APP_WALLET_CONNECT_PROJECT_ID; export const queryClient = new QueryClient(); -export const wagmiMetadata = { +const metadata = { name: import.meta.env.VITE_APP_NAME, description: 'Are you outgrowing your Multisig? Decent extends Safe treasuries into on-chain hierarchies of permissions, token flows, and governance.', @@ -21,3 +27,17 @@ export const transportsReducer = ( accumulator[network.chain.id] = http(network.rpcEndpoint); return accumulator; }; + +export const wagmiConfig = defaultWagmiConfig({ + chains: supportedWagmiChains as [Chain, ...Chain[]], + projectId: walletConnectProjectId, + metadata, + transports: supportedNetworks.reduce(transportsReducer, {}), + batch: { + multicall: true, + }, +}); + +if (walletConnectProjectId) { + createWeb3Modal({ wagmiConfig, projectId: walletConnectProjectId, metadata: metadata }); +} From 3d324ce768b0604cc1c0edb9d35bdf1019433afb Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:07:30 -0500 Subject: [PATCH 20/48] Refactor useHatsTree and useKeyValuePairs hooks to remove unused variables and improve logic --- src/hooks/DAO/loaders/useHatsTree.ts | 8 ++------ src/hooks/DAO/useKeyValuePairs.ts | 7 +------ 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/hooks/DAO/loaders/useHatsTree.ts b/src/hooks/DAO/loaders/useHatsTree.ts index 0dbe4d0e27..b6b181c973 100644 --- a/src/hooks/DAO/loaders/useHatsTree.ts +++ b/src/hooks/DAO/loaders/useHatsTree.ts @@ -10,27 +10,23 @@ import { SablierV2LockupLinearAbi } from '../../../assets/abi/SablierV2LockupLin import { useFractal } from '../../../providers/App/AppProvider'; import useIPFSClient from '../../../providers/App/hooks/useIPFSClient'; import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; -import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { DecentHatsError } from '../../../store/roles/rolesStoreUtils'; import { useRolesStore } from '../../../store/roles/useRolesStore'; import { SablierPayment } from '../../../types/roles'; import { convertStreamIdToBigInt } from '../../streams/useCreateSablierStream'; import { CacheExpiry, CacheKeys } from '../../utils/cache/cacheDefaults'; import { getValue, setValue } from '../../utils/cache/useLocalStorage'; -import { useParseSafeAddress } from '../useParseSafeAddress'; const hatsSubgraphClient = new HatsSubgraphClient({}); const useHatsTree = () => { const { t } = useTranslation('roles'); - const { safeAddress } = useParseSafeAddress(); const { governanceContracts: { linearVotingErc20WithHatsWhitelistingAddress, linearVotingErc721WithHatsWhitelistingAddress, }, } = useFractal(); - const { safe } = useDaoInfoStore(); const { hatsTreeId, contextChainId, @@ -297,10 +293,10 @@ const useHatsTree = () => { }, [hatsTree, updateRolesWithStreams, getPaymentStreams, streamsFetched]); useEffect(() => { - if (safeAddress && safe?.address && safeAddress !== safe.address && hatsTree) { + if (!hatsTreeId && !!hatsTree) { resetHatsStore(); } - }, [resetHatsStore, safeAddress, safe?.address, hatsTree]); + }, [resetHatsStore, hatsTree, hatsTreeId]); }; export { useHatsTree }; diff --git a/src/hooks/DAO/useKeyValuePairs.ts b/src/hooks/DAO/useKeyValuePairs.ts index 38615ef52c..e850e1bf4e 100644 --- a/src/hooks/DAO/useKeyValuePairs.ts +++ b/src/hooks/DAO/useKeyValuePairs.ts @@ -1,7 +1,6 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { hatIdToTreeId } from '@hatsprotocol/sdk-v1-core'; import { useEffect } from 'react'; -import { useSearchParams } from 'react-router-dom'; import { Address, GetContractEventsReturnType, getContract } from 'viem'; import { usePublicClient } from 'wagmi'; import { logError } from '../../helpers/errorLogging'; @@ -99,14 +98,11 @@ const useKeyValuePairs = () => { contracts: { keyValuePairs, sablierV2LockupLinear }, } = useNetworkConfig(); const { setHatKeyValuePairData } = useRolesStore(); - const [searchParams] = useSearchParams(); const safeAddress = node.safe?.address; useEffect(() => { - const safeParam = searchParams.get('dao'); - - if (!publicClient || !safeAddress || safeAddress !== safeParam?.split(':')[1]) { + if (!safeAddress || !publicClient) { return; } @@ -160,7 +156,6 @@ const useKeyValuePairs = () => { keyValuePairs, safeAddress, publicClient, - searchParams, setHatKeyValuePairData, sablierV2LockupLinear, ]); From d1f5bb0d94239282ad6a264c4ee740ac9c9729d3 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:07:33 -0500 Subject: [PATCH 21/48] Add error handling for smart account predictions and validate smart addresses in role creation --- src/hooks/utils/useCreateRoles.ts | 3 +++ src/store/roles/rolesStoreUtils.ts | 9 ++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/hooks/utils/useCreateRoles.ts b/src/hooks/utils/useCreateRoles.ts index db6e8ad62c..be978509bf 100644 --- a/src/hooks/utils/useCreateRoles.ts +++ b/src/hooks/utils/useCreateRoles.ts @@ -915,6 +915,9 @@ export default function useCreateRoles() { throw new Error('Cannot prepare transactions for edited role without smart address'); } const newPredictedHatSmartAccount = await predictSmartAccount(BigInt(formHat.id)); + if (!newPredictedHatSmartAccount) { + throw new Error('Cannot predict smart account'); + } const newStreamTxData = createBatchLinearStreamCreationTx( newStreamsOnHat.map(stream => ({ ...stream, recipient: newPredictedHatSmartAccount })), ); diff --git a/src/store/roles/rolesStoreUtils.ts b/src/store/roles/rolesStoreUtils.ts index 5db97f48a0..166bec4b62 100644 --- a/src/store/roles/rolesStoreUtils.ts +++ b/src/store/roles/rolesStoreUtils.ts @@ -134,7 +134,7 @@ export const predictAccountAddress = async (params: { tokenId, ]); if (!(await publicClient.getBytecode({ address: predictedAddress }))) { - throw new DecentHatsError('Predicted address is not a contract'); + return; } return predictedAddress; }; @@ -288,6 +288,10 @@ export const sanitize = async ( publicClient, }); + if (!topHatSmartAddress) { + throw new DecentHatsError('Top Hat smart address is not valid'); + } + const whitelistingVotingContract = whitelistingVotingStrategy ? getContract({ abi: abis.LinearERC20VotingWithHatsProposalCreation, @@ -320,6 +324,9 @@ export const sanitize = async ( tokenId: BigInt(rawAdminHat.id), publicClient, }); + if (!adminHatSmartAddress) { + throw new DecentHatsError('Admin Hat smart address is not valid'); + } const adminHat: DecentAdminHat = { id: rawAdminHat.id, From fb9c75139e52f5c86836ea969f0a1191ef60fd2c Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:53:06 -0500 Subject: [PATCH 22/48] feat: integrate chain switching functionality in EstablishEssentials component --- .../formComponents/EstablishEssentials.tsx | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/components/DaoCreator/formComponents/EstablishEssentials.tsx b/src/components/DaoCreator/formComponents/EstablishEssentials.tsx index 1db69f86ea..aa917e26c9 100644 --- a/src/components/DaoCreator/formComponents/EstablishEssentials.tsx +++ b/src/components/DaoCreator/formComponents/EstablishEssentials.tsx @@ -3,6 +3,7 @@ import { CheckCircle } from '@phosphor-icons/react'; import debounce from 'lodash.debounce'; import { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { useChainId, useSwitchChain } from 'wagmi'; import { createAccountSubstring } from '../../../hooks/utils/useGetAccountName'; import { supportedNetworks, @@ -10,7 +11,7 @@ import { } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { GovernanceType, ICreationStepProps, VotingStrategyType } from '../../../types'; -import { getNetworkIcon } from '../../../utils/url'; +import { getChainIdFromPrefix, getNetworkIcon } from '../../../utils/url'; import { InputComponent, LabelComponent } from '../../ui/forms/InputComponent'; import LabelWrapper from '../../ui/forms/LabelWrapper'; import { RadioWithText } from '../../ui/forms/Radio/RadioWithText'; @@ -71,7 +72,9 @@ export function EstablishEssentials(props: ICreationStepProps) { setFieldValue('essentials.governance', value); }; - const { createOptions, setCurrentConfig, chain, getConfigByChainId } = useNetworkConfigStore(); + const { createOptions, setCurrentConfig, chain, getConfigByChainId, addressPrefix } = + useNetworkConfigStore(); + const walletChainID = useChainId(); const [snapshotENSInput, setSnapshotENSInput] = useState(''); @@ -94,6 +97,17 @@ export function EstablishEssentials(props: ICreationStepProps) { selected: chain.id === network.chain.id, })); + const { switchChain } = useSwitchChain({ + mutation: { + onError: () => { + if (chain.id !== walletChainID) { + const chainId = getChainIdFromPrefix(addressPrefix); + switchChain({ chainId }); + } + }, + }, + }); + return ( <> Date: Tue, 10 Dec 2024 19:58:50 -0500 Subject: [PATCH 23/48] Update useHatsTree hook to accept safeAddress parameter and adjust resetStore conditional to just reset store when safeAddress changes --- src/hooks/DAO/loaders/useHatsTree.ts | 6 +++--- src/pages/dao/SafeController.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hooks/DAO/loaders/useHatsTree.ts b/src/hooks/DAO/loaders/useHatsTree.ts index b6b181c973..693824e08f 100644 --- a/src/hooks/DAO/loaders/useHatsTree.ts +++ b/src/hooks/DAO/loaders/useHatsTree.ts @@ -19,7 +19,7 @@ import { getValue, setValue } from '../../utils/cache/useLocalStorage'; const hatsSubgraphClient = new HatsSubgraphClient({}); -const useHatsTree = () => { +const useHatsTree = ({ safeAddress }: { safeAddress: Address | undefined }) => { const { t } = useTranslation('roles'); const { governanceContracts: { @@ -293,10 +293,10 @@ const useHatsTree = () => { }, [hatsTree, updateRolesWithStreams, getPaymentStreams, streamsFetched]); useEffect(() => { - if (!hatsTreeId && !!hatsTree) { + if (safeAddress === undefined) { resetHatsStore(); } - }, [resetHatsStore, hatsTree, hatsTreeId]); + }, [resetHatsStore, safeAddress]); }; export { useHatsTree }; diff --git a/src/pages/dao/SafeController.tsx b/src/pages/dao/SafeController.tsx index 2f3c92f688..6b6ac3c997 100644 --- a/src/pages/dao/SafeController.tsx +++ b/src/pages/dao/SafeController.tsx @@ -43,7 +43,7 @@ export function SafeController() { useAzoriusListeners(); useKeyValuePairs(); - useHatsTree(); + useHatsTree({ safeAddress }); // the order of the if blocks of these next three error states matters if (invalidQuery) { From 23cab883962eaf93a62bd8fb23f73fb0cec524dd Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 12 Dec 2024 00:50:03 -0500 Subject: [PATCH 24/48] Delay setting current config after switching chain to ensure proper state update --- src/hooks/utils/useAutomaticSwitchChain.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/utils/useAutomaticSwitchChain.ts b/src/hooks/utils/useAutomaticSwitchChain.ts index 2459cc18ee..ec7cd394a2 100644 --- a/src/hooks/utils/useAutomaticSwitchChain.ts +++ b/src/hooks/utils/useAutomaticSwitchChain.ts @@ -25,9 +25,9 @@ export const useAutomaticSwitchChain = ({ return; } const chainId = getChainIdFromPrefix(urlAddressPrefix); - setCurrentConfig(getConfigByChainId(chainId)); if (addressPrefix !== urlAddressPrefix && urlAddressPrefix !== undefined) { switchChain({ chainId }); } + setTimeout(() => setCurrentConfig(getConfigByChainId(chainId)), 300); }, [addressPrefix, setCurrentConfig, getConfigByChainId, urlAddressPrefix, switchChain]); }; From 9094eb3836cb700bebde9318fb5cbc60baec80bd Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 12 Dec 2024 00:51:16 -0500 Subject: [PATCH 25/48] Remove SafesList component and its associated logic --- .../ui/menus/SafesMenu/SafesList.tsx | 47 ------------------- 1 file changed, 47 deletions(-) delete mode 100644 src/components/ui/menus/SafesMenu/SafesList.tsx diff --git a/src/components/ui/menus/SafesMenu/SafesList.tsx b/src/components/ui/menus/SafesMenu/SafesList.tsx deleted file mode 100644 index d3547ae043..0000000000 --- a/src/components/ui/menus/SafesMenu/SafesList.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { Box, MenuList } from '@chakra-ui/react'; -import { useTranslation } from 'react-i18next'; -import { NEUTRAL_2_82_TRANSPARENT } from '../../../../constants/common'; -import { useAccountFavorites } from '../../../../hooks/DAO/loaders/useFavorites'; -import Divider from '../../utils/Divider'; -import { ErrorBoundary } from '../../utils/ErrorBoundary'; -import { MySafesErrorFallback } from '../../utils/MySafesErrorFallback'; -import { SafeMenuItem } from './SafeMenuItem'; - -export function SafesList() { - const { favoritesList } = useAccountFavorites(); - - const { t } = useTranslation('dashboard'); - return ( - - - - {favoritesList.length === 0 ? ( - {t('emptyFavorites')} - ) : ( - favoritesList.map((favorite, i) => ( - - - {favoritesList.length - 1 !== i && } - - )) - )} - - - - ); -} From 17bc618d0246449f87753192564e31cbfb833d80 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 12 Dec 2024 00:51:39 -0500 Subject: [PATCH 26/48] Add null check for modules and improve safeAddress validation in useGovernanceContracts hook --- src/hooks/DAO/loaders/useGovernanceContracts.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/hooks/DAO/loaders/useGovernanceContracts.ts b/src/hooks/DAO/loaders/useGovernanceContracts.ts index 5a6436e19d..412b8c5e66 100644 --- a/src/hooks/DAO/loaders/useGovernanceContracts.ts +++ b/src/hooks/DAO/loaders/useGovernanceContracts.ts @@ -24,7 +24,10 @@ export const useGovernanceContracts = () => { const safeAddress = safe?.address; const loadGovernanceContracts = useCallback(async () => { - const azoriusModule = getAzoriusModuleFromModules(modules ?? []); + if (!modules) { + return; + } + const azoriusModule = getAzoriusModuleFromModules(modules); const votingStrategies = await getVotingStrategies(); @@ -131,7 +134,11 @@ export const useGovernanceContracts = () => { }, [action, modules, getVotingStrategies, publicClient, getAddressContractType]); useEffect(() => { - if (currentValidAddress.current !== safeAddress && modules !== null) { + if ( + safeAddress !== undefined && + currentValidAddress.current !== safeAddress && + modules !== null + ) { loadGovernanceContracts(); currentValidAddress.current = safeAddress; } From 32c99617b7b3b93c77de0741407cda579626265d Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 12 Dec 2024 00:54:31 -0500 Subject: [PATCH 27/48] Add error handling for uninitialized modules in useGovernanceContracts hook --- src/hooks/DAO/loaders/useGovernanceContracts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/DAO/loaders/useGovernanceContracts.ts b/src/hooks/DAO/loaders/useGovernanceContracts.ts index 412b8c5e66..0463eaf1b2 100644 --- a/src/hooks/DAO/loaders/useGovernanceContracts.ts +++ b/src/hooks/DAO/loaders/useGovernanceContracts.ts @@ -25,7 +25,7 @@ export const useGovernanceContracts = () => { const loadGovernanceContracts = useCallback(async () => { if (!modules) { - return; + throw new Error('DAO modules not ready'); } const azoriusModule = getAzoriusModuleFromModules(modules); From d76ec5bd21c53e6ee61b23341ab1030213e2954d Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 12 Dec 2024 01:05:53 -0500 Subject: [PATCH 28/48] Refactor loadGovernanceContracts to accept daoModules parameter --- .../DAO/loaders/useGovernanceContracts.ts | 205 +++++++++--------- 1 file changed, 103 insertions(+), 102 deletions(-) diff --git a/src/hooks/DAO/loaders/useGovernanceContracts.ts b/src/hooks/DAO/loaders/useGovernanceContracts.ts index 0463eaf1b2..4c2ae912d8 100644 --- a/src/hooks/DAO/loaders/useGovernanceContracts.ts +++ b/src/hooks/DAO/loaders/useGovernanceContracts.ts @@ -6,6 +6,7 @@ import LockReleaseAbi from '../../../assets/abi/LockRelease'; import { useFractal } from '../../../providers/App/AppProvider'; import { GovernanceContractAction } from '../../../providers/App/governanceContracts/action'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; +import { DecentModule } from '../../../types'; import { getAzoriusModuleFromModules } from '../../../utils'; import { useAddressContractType } from '../../utils/useAddressContractType'; import useVotingStrategyAddress from '../../utils/useVotingStrategiesAddresses'; @@ -23,115 +24,115 @@ export const useGovernanceContracts = () => { const safeAddress = safe?.address; - const loadGovernanceContracts = useCallback(async () => { - if (!modules) { - throw new Error('DAO modules not ready'); - } - const azoriusModule = getAzoriusModuleFromModules(modules); - - const votingStrategies = await getVotingStrategies(); + const loadGovernanceContracts = useCallback( + async (daoModules: DecentModule[]) => { + const azoriusModule = getAzoriusModuleFromModules(daoModules); - if (!azoriusModule || !votingStrategies) { - action.dispatch({ - type: GovernanceContractAction.SET_GOVERNANCE_CONTRACT_ADDRESSES, - payload: {}, - }); - return; - } - - if (!publicClient) { - throw new Error('Public Client is not set!'); - } + const votingStrategies = await getVotingStrategies(); - let linearVotingErc20Address: Address | undefined; - let linearVotingErc721Address: Address | undefined; - let linearVotingErc20WithHatsWhitelistingAddress: Address | undefined; - let linearVotingErc721WithHatsWhitelistingAddress: Address | undefined; - let votesTokenAddress: Address | undefined; - let underlyingTokenAddress: Address | undefined; - let lockReleaseAddress: Address | undefined; - - const setGovTokenAddress = async (erc20VotingStrategyAddress: Address) => { - if (votesTokenAddress) { + if (!azoriusModule || !votingStrategies) { + action.dispatch({ + type: GovernanceContractAction.SET_GOVERNANCE_CONTRACT_ADDRESSES, + payload: {}, + }); return; } - const ozLinearVotingContract = getContract({ - abi: abis.LinearERC20Voting, - address: erc20VotingStrategyAddress, - client: publicClient, - }); - - const govTokenAddress = await ozLinearVotingContract.read.governanceToken(); - // govTokenAddress might be either - // - a valid VotesERC20 contract - // - a valid LockRelease contract - // - or none of these which is against business logic - - const { isVotesErc20 } = await getAddressContractType(govTokenAddress); - - if (isVotesErc20) { - votesTokenAddress = govTokenAddress; - } else { - const possibleLockRelease = getContract({ - address: govTokenAddress, - abi: LockReleaseAbi, - client: { public: publicClient }, - }); - try { - const lockedTokenAddress = await possibleLockRelease.read.token(); - lockReleaseAddress = govTokenAddress; - votesTokenAddress = lockedTokenAddress; - } catch { - throw new Error('Unknown governance token type'); - } + if (!publicClient) { + throw new Error('Public Client is not set!'); } - }; - - await Promise.all( - votingStrategies.map(async votingStrategy => { - const { - strategyAddress, - isLinearVotingErc20, - isLinearVotingErc721, - isLinearVotingErc20WithHatsProposalCreation, - isLinearVotingErc721WithHatsProposalCreation, - } = votingStrategy; - if (isLinearVotingErc20) { - linearVotingErc20Address = strategyAddress; - await setGovTokenAddress(strategyAddress); - } else if (isLinearVotingErc721) { - linearVotingErc721Address = strategyAddress; - } else if (isLinearVotingErc20WithHatsProposalCreation) { - linearVotingErc20WithHatsWhitelistingAddress = strategyAddress; - await setGovTokenAddress(strategyAddress); - } else if (isLinearVotingErc721WithHatsProposalCreation) { - linearVotingErc721WithHatsWhitelistingAddress = strategyAddress; + + let linearVotingErc20Address: Address | undefined; + let linearVotingErc721Address: Address | undefined; + let linearVotingErc20WithHatsWhitelistingAddress: Address | undefined; + let linearVotingErc721WithHatsWhitelistingAddress: Address | undefined; + let votesTokenAddress: Address | undefined; + let underlyingTokenAddress: Address | undefined; + let lockReleaseAddress: Address | undefined; + + const setGovTokenAddress = async (erc20VotingStrategyAddress: Address) => { + if (votesTokenAddress) { + return; } - }), - ); + const ozLinearVotingContract = getContract({ + abi: abis.LinearERC20Voting, + address: erc20VotingStrategyAddress, + client: publicClient, + }); - if ( - linearVotingErc20Address || - linearVotingErc20WithHatsWhitelistingAddress || - linearVotingErc721Address || - linearVotingErc721WithHatsWhitelistingAddress - ) { - action.dispatch({ - type: GovernanceContractAction.SET_GOVERNANCE_CONTRACT_ADDRESSES, - payload: { - linearVotingErc20Address, - linearVotingErc20WithHatsWhitelistingAddress, - linearVotingErc721Address, - linearVotingErc721WithHatsWhitelistingAddress, - votesTokenAddress, - underlyingTokenAddress, - lockReleaseAddress, - moduleAzoriusAddress: azoriusModule.moduleAddress, - }, - }); - } - }, [action, modules, getVotingStrategies, publicClient, getAddressContractType]); + const govTokenAddress = await ozLinearVotingContract.read.governanceToken(); + // govTokenAddress might be either + // - a valid VotesERC20 contract + // - a valid LockRelease contract + // - or none of these which is against business logic + + const { isVotesErc20 } = await getAddressContractType(govTokenAddress); + + if (isVotesErc20) { + votesTokenAddress = govTokenAddress; + } else { + const possibleLockRelease = getContract({ + address: govTokenAddress, + abi: LockReleaseAbi, + client: { public: publicClient }, + }); + + try { + const lockedTokenAddress = await possibleLockRelease.read.token(); + lockReleaseAddress = govTokenAddress; + votesTokenAddress = lockedTokenAddress; + } catch { + throw new Error('Unknown governance token type'); + } + } + }; + + await Promise.all( + votingStrategies.map(async votingStrategy => { + const { + strategyAddress, + isLinearVotingErc20, + isLinearVotingErc721, + isLinearVotingErc20WithHatsProposalCreation, + isLinearVotingErc721WithHatsProposalCreation, + } = votingStrategy; + if (isLinearVotingErc20) { + linearVotingErc20Address = strategyAddress; + await setGovTokenAddress(strategyAddress); + } else if (isLinearVotingErc721) { + linearVotingErc721Address = strategyAddress; + } else if (isLinearVotingErc20WithHatsProposalCreation) { + linearVotingErc20WithHatsWhitelistingAddress = strategyAddress; + await setGovTokenAddress(strategyAddress); + } else if (isLinearVotingErc721WithHatsProposalCreation) { + linearVotingErc721WithHatsWhitelistingAddress = strategyAddress; + } + }), + ); + + if ( + linearVotingErc20Address || + linearVotingErc20WithHatsWhitelistingAddress || + linearVotingErc721Address || + linearVotingErc721WithHatsWhitelistingAddress + ) { + action.dispatch({ + type: GovernanceContractAction.SET_GOVERNANCE_CONTRACT_ADDRESSES, + payload: { + linearVotingErc20Address, + linearVotingErc20WithHatsWhitelistingAddress, + linearVotingErc721Address, + linearVotingErc721WithHatsWhitelistingAddress, + votesTokenAddress, + underlyingTokenAddress, + lockReleaseAddress, + moduleAzoriusAddress: azoriusModule.moduleAddress, + }, + }); + } + }, + [action, getVotingStrategies, publicClient, getAddressContractType], + ); useEffect(() => { if ( @@ -139,7 +140,7 @@ export const useGovernanceContracts = () => { currentValidAddress.current !== safeAddress && modules !== null ) { - loadGovernanceContracts(); + loadGovernanceContracts(modules); currentValidAddress.current = safeAddress; } if (!safeAddress) { From 113d28f907169d4e4e54cd75bfbec08a919821fe Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 13 Dec 2024 00:11:17 -0500 Subject: [PATCH 29/48] Update error message for invalid DAO search to reflect support for all chains --- src/i18n/locales/en/dashboard.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/locales/en/dashboard.json b/src/i18n/locales/en/dashboard.json index 0c2d403a4f..567b53e56c 100644 --- a/src/i18n/locales/en/dashboard.json +++ b/src/i18n/locales/en/dashboard.json @@ -2,7 +2,7 @@ "emptyFavorites": "You haven't added any DAOs yet.", "loadingFavorite": "Loading DAO Name", "errorInvalidSearch": "Oops! This Ethereum address is invalid.", - "errorFailedSearch": "Sorry, this address is not a DAO on {{chain}}.", + "errorFailedSearch": "Sorry, this address is not a DAO on any supported chain.", "searchDAOPlaceholder": "Enter Address or ENS Name", "titleGovernance": "Governance", "titleType": "Type", From d2319e573f360248b283f9edc8df393916c3075d Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 13 Dec 2024 00:11:29 -0500 Subject: [PATCH 30/48] Enhance useIsSafe hook to search across all supported networks and return found network prefixes --- src/hooks/DAO/useSearchDao.ts | 9 ++++---- src/hooks/safe/useIsSafe.ts | 39 ++++++++++++++++++++++++++--------- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/hooks/DAO/useSearchDao.ts b/src/hooks/DAO/useSearchDao.ts index 74a42a57d2..c80ba5d126 100644 --- a/src/hooks/DAO/useSearchDao.ts +++ b/src/hooks/DAO/useSearchDao.ts @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { useIsSafe } from '../safe/useIsSafe'; @@ -7,9 +7,10 @@ import useAddress from '../utils/useAddress'; export const useSearchDao = () => { const [searchString, setSearchString] = useState(''); const [errorMessage, setErrorMessage] = useState(); + // This hook needs to search all supoorted chains for the address const { address, isValid, isLoading: isAddressLoading } = useAddress(searchString); - const { isSafe, isSafeLoading } = useIsSafe(address); + const { isSafe, isSafeLoading, safeFoundNetworkPrefixes } = useIsSafe(address); const { t } = useTranslation('dashboard'); const { chain } = useNetworkConfigStore(); @@ -21,15 +22,15 @@ export const useSearchDao = () => { if (searchString === '' || isLoading || isSafe || isValid === undefined) { return; } - if (isValid === true) { - setErrorMessage(t('errorFailedSearch', { chain: chain.name })); + setErrorMessage(t('errorFailedSearch')); } else { setErrorMessage(t('errorInvalidSearch')); } }, [chain.name, isLoading, isSafe, isValid, searchString, t]); return { + safeFoundNetworkPrefixes, errorMessage, isLoading, address, diff --git a/src/hooks/safe/useIsSafe.ts b/src/hooks/safe/useIsSafe.ts index a5a3bdf81a..c03704bab6 100644 --- a/src/hooks/safe/useIsSafe.ts +++ b/src/hooks/safe/useIsSafe.ts @@ -1,6 +1,7 @@ -import { useEffect, useState } from 'react'; +import SafeApiKit from '@safe-global/api-kit'; +import { useCallback, useEffect, useState } from 'react'; import { isAddress } from 'viem'; -import { useSafeAPI } from '../../providers/App/hooks/useSafeAPI'; +import { supportedNetworks } from '../../providers/NetworkConfig/useNetworkConfigStore'; /** * A hook which determines whether the provided Ethereum address is a Safe @@ -15,25 +16,43 @@ import { useSafeAPI } from '../../providers/App/hooks/useSafeAPI'; */ export const useIsSafe = (address: string | undefined) => { const [isSafeLoading, setSafeLoading] = useState(false); - const [isSafe, setIsSafe] = useState(); - const safeAPI = useSafeAPI(); + const [isSafe, setIsSafe] = useState(undefined); + const [safeFoundNetworkPrefixes, setNetworkPrefixes] = useState([]); + + const findSafes = useCallback(async (_address: string) => { + const networkPrefixes = []; // address prefixes + for await (const network of supportedNetworks) { + const safeAPI = new SafeApiKit({ chainId: BigInt(network.chain.id) }); + safeAPI.getSafeCreationInfo(_address); + try { + await safeAPI.getSafeCreationInfo(_address); + networkPrefixes.push(network.addressPrefix); + } catch (e) { + // Safe not found + continue; + } + } + return [networkPrefixes, networkPrefixes.length > 0] as const; // [networks, isSafe] + }, []); useEffect(() => { setSafeLoading(true); setIsSafe(undefined); - if (!address || !isAddress(address) || !safeAPI) { + if (!address || !isAddress(address)) { setIsSafe(false); setSafeLoading(false); return; } - safeAPI - .getSafeCreationInfo(address) - .then(() => setIsSafe(true)) + findSafes(address) + .then(([_safeFoundNetworkPrefixes, _isSafe]) => { + setNetworkPrefixes(_safeFoundNetworkPrefixes); + setIsSafe(_isSafe); + }) .catch(() => setIsSafe(false)) .finally(() => setSafeLoading(false)); - }, [address, safeAPI]); + }, [address, findSafes]); - return { isSafe, isSafeLoading }; + return { isSafe, isSafeLoading, safeFoundNetworkPrefixes }; }; From 2bb83c175d133e66be5e80d160a8cfba8e5f5873 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 13 Dec 2024 00:11:33 -0500 Subject: [PATCH 31/48] Refactor DAOSearch to display multiple network prefixes in SearchDisplay component --- .../ui/menus/DAOSearch/SearchDisplay.tsx | 13 +++++++--- src/components/ui/menus/DAOSearch/index.tsx | 25 +++++++++++++------ 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/components/ui/menus/DAOSearch/SearchDisplay.tsx b/src/components/ui/menus/DAOSearch/SearchDisplay.tsx index aa1a7ac85f..3e02346a56 100644 --- a/src/components/ui/menus/DAOSearch/SearchDisplay.tsx +++ b/src/components/ui/menus/DAOSearch/SearchDisplay.tsx @@ -4,7 +4,6 @@ import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Address } from 'viem'; import { SafeDisplayRow } from '../../../../pages/home/SafeDisplayRow'; -import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import { ErrorBoundary } from '../../utils/ErrorBoundary'; import { MySafesErrorFallback } from '../../utils/MySafesErrorFallback'; @@ -14,12 +13,18 @@ interface ISearchDisplay { errorMessage: string | undefined; address: Address | undefined; onClickView: Function; + networkPrefix: string; } -export function SearchDisplay({ loading, errorMessage, address, onClickView }: ISearchDisplay) { +export function SearchDisplay({ + loading, + errorMessage, + address, + onClickView, + networkPrefix, +}: ISearchDisplay) { const { t } = useTranslation(['common', 'dashboard']); const node = useDaoInfoStore(); - const { addressPrefix } = useNetworkConfigStore(); const isCurrentSafe = useMemo( () => !!node && !!node?.safe?.address && node.safe.address === address, @@ -85,7 +90,7 @@ export function SearchDisplay({ loading, errorMessage, address, onClickView }: I { onClickView(); }} diff --git a/src/components/ui/menus/DAOSearch/index.tsx b/src/components/ui/menus/DAOSearch/index.tsx index a1a2409f02..ab4e778689 100644 --- a/src/components/ui/menus/DAOSearch/index.tsx +++ b/src/components/ui/menus/DAOSearch/index.tsx @@ -8,6 +8,7 @@ import { useDisclosure, useOutsideClick, Portal, + Flex, } from '@chakra-ui/react'; import debounce from 'lodash.debounce'; import { useEffect, useMemo, useRef, useState } from 'react'; @@ -21,7 +22,8 @@ export function DAOSearch() { const { t } = useTranslation(['dashboard']); const [localInput, setLocalInput] = useState(''); const [typing, setTyping] = useState(false); - const { errorMessage, isLoading, address, setSearchString } = useSearchDao(); + const { errorMessage, isLoading, address, setSearchString, safeFoundNetworkPrefixes } = + useSearchDao(); const { isOpen, onOpen, onClose } = useDisclosure(); const ref = useRef(null); @@ -149,12 +151,21 @@ export function DAOSearch() { w="full" position="absolute" > - + {safeFoundNetworkPrefixes.map(networkPrefix => ( + + + + ))} From 8dab85b4a9e11ec28b0979dd14e68a3be895d286 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 13 Dec 2024 00:27:10 -0500 Subject: [PATCH 32/48] Refactor DAOSearch component to remove extra gap --- src/components/ui/menus/DAOSearch/index.tsx | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/components/ui/menus/DAOSearch/index.tsx b/src/components/ui/menus/DAOSearch/index.tsx index ab4e778689..9c11123910 100644 --- a/src/components/ui/menus/DAOSearch/index.tsx +++ b/src/components/ui/menus/DAOSearch/index.tsx @@ -8,7 +8,6 @@ import { useDisclosure, useOutsideClick, Portal, - Flex, } from '@chakra-ui/react'; import debounce from 'lodash.debounce'; import { useEffect, useMemo, useRef, useState } from 'react'; @@ -152,19 +151,14 @@ export function DAOSearch() { position="absolute" > {safeFoundNetworkPrefixes.map(networkPrefix => ( - - - + loading={isLoading} + errorMessage={errorMessage} + address={address} + networkPrefix={networkPrefix} + onClickView={resetSearch} + /> ))} From 25866dbb0c3cb2983c937c052c7cdbe0911137bd Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:41:07 -0500 Subject: [PATCH 33/48] Refactor useGovernanceContracts to streamline token address assignment and remove unused variable --- src/hooks/DAO/loaders/useGovernanceContracts.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/hooks/DAO/loaders/useGovernanceContracts.ts b/src/hooks/DAO/loaders/useGovernanceContracts.ts index 4c2ae912d8..1c672604dc 100644 --- a/src/hooks/DAO/loaders/useGovernanceContracts.ts +++ b/src/hooks/DAO/loaders/useGovernanceContracts.ts @@ -29,7 +29,6 @@ export const useGovernanceContracts = () => { const azoriusModule = getAzoriusModuleFromModules(daoModules); const votingStrategies = await getVotingStrategies(); - if (!azoriusModule || !votingStrategies) { action.dispatch({ type: GovernanceContractAction.SET_GOVERNANCE_CONTRACT_ADDRESSES, @@ -78,9 +77,8 @@ export const useGovernanceContracts = () => { }); try { - const lockedTokenAddress = await possibleLockRelease.read.token(); + votesTokenAddress = await possibleLockRelease.read.token(); lockReleaseAddress = govTokenAddress; - votesTokenAddress = lockedTokenAddress; } catch { throw new Error('Unknown governance token type'); } From ef7a3de210b99ceb74919e59d22e9513fdcf2f76 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 13 Dec 2024 15:53:14 -0500 Subject: [PATCH 34/48] revert: changes made to hook. --- src/hooks/safe/useIsSafe.ts | 39 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/src/hooks/safe/useIsSafe.ts b/src/hooks/safe/useIsSafe.ts index c03704bab6..a5a3bdf81a 100644 --- a/src/hooks/safe/useIsSafe.ts +++ b/src/hooks/safe/useIsSafe.ts @@ -1,7 +1,6 @@ -import SafeApiKit from '@safe-global/api-kit'; -import { useCallback, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { isAddress } from 'viem'; -import { supportedNetworks } from '../../providers/NetworkConfig/useNetworkConfigStore'; +import { useSafeAPI } from '../../providers/App/hooks/useSafeAPI'; /** * A hook which determines whether the provided Ethereum address is a Safe @@ -16,43 +15,25 @@ import { supportedNetworks } from '../../providers/NetworkConfig/useNetworkConfi */ export const useIsSafe = (address: string | undefined) => { const [isSafeLoading, setSafeLoading] = useState(false); - const [isSafe, setIsSafe] = useState(undefined); - const [safeFoundNetworkPrefixes, setNetworkPrefixes] = useState([]); - - const findSafes = useCallback(async (_address: string) => { - const networkPrefixes = []; // address prefixes - for await (const network of supportedNetworks) { - const safeAPI = new SafeApiKit({ chainId: BigInt(network.chain.id) }); - safeAPI.getSafeCreationInfo(_address); - try { - await safeAPI.getSafeCreationInfo(_address); - networkPrefixes.push(network.addressPrefix); - } catch (e) { - // Safe not found - continue; - } - } - return [networkPrefixes, networkPrefixes.length > 0] as const; // [networks, isSafe] - }, []); + const [isSafe, setIsSafe] = useState(); + const safeAPI = useSafeAPI(); useEffect(() => { setSafeLoading(true); setIsSafe(undefined); - if (!address || !isAddress(address)) { + if (!address || !isAddress(address) || !safeAPI) { setIsSafe(false); setSafeLoading(false); return; } - findSafes(address) - .then(([_safeFoundNetworkPrefixes, _isSafe]) => { - setNetworkPrefixes(_safeFoundNetworkPrefixes); - setIsSafe(_isSafe); - }) + safeAPI + .getSafeCreationInfo(address) + .then(() => setIsSafe(true)) .catch(() => setIsSafe(false)) .finally(() => setSafeLoading(false)); - }, [address, findSafes]); + }, [address, safeAPI]); - return { isSafe, isSafeLoading, safeFoundNetworkPrefixes }; + return { isSafe, isSafeLoading }; }; From 2118a5dde307a9c643384f95d87706bfe4afa251 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 13 Dec 2024 20:16:30 -0500 Subject: [PATCH 35/48] Add useResolveAddressMultiChain hook for multi-chain address resolution --- .../utils/useResolveAddressMultiChain.ts | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/hooks/utils/useResolveAddressMultiChain.ts diff --git a/src/hooks/utils/useResolveAddressMultiChain.ts b/src/hooks/utils/useResolveAddressMultiChain.ts new file mode 100644 index 0000000000..f3926b1e53 --- /dev/null +++ b/src/hooks/utils/useResolveAddressMultiChain.ts @@ -0,0 +1,72 @@ +import { useState, useCallback } from 'react'; +import { Address, createPublicClient, http, isAddress, getAddress } from 'viem'; +import { normalize } from 'viem/ens'; +import { supportedNetworks } from '../../providers/NetworkConfig/useNetworkConfigStore'; + +type ResolveAddressReturnType = { + resolved: { + address: Address; + chainId: number; + }[]; + isValid: boolean; +}; +export const useResolveAddressMultiChain = () => { + const [isLoading, setIsLoading] = useState(false); + + const resolveAddressMultiChain = useCallback( + async (input: string): Promise => { + setIsLoading(true); + + const returnedResult: ResolveAddressReturnType = { + resolved: [], + isValid: false, + }; + + if (input === '') { + throw new Error('ENS name is empty'); + } + + if (isAddress(input)) { + // @dev if its a valid address, its valid on all networks + returnedResult.isValid = true; + returnedResult.resolved = supportedNetworks.map(network => ({ + address: getAddress(input), + chainId: network.chain.id, + })); + setIsLoading(false); + return returnedResult; + } + + // @dev if its not an address, try to resolve as possible ENS name on all networks + let normalizedName: string; + try { + normalizedName = normalize(input); + } catch { + setIsLoading(false); + return returnedResult; + } + for (const network of supportedNetworks) { + const client = createPublicClient({ + chain: network.chain, + transport: http(network.rpcEndpoint), + }); + try { + const resolvedAddress = await client.getEnsAddress({ name: normalizedName }); + if (resolvedAddress) { + returnedResult.resolved.push({ + address: resolvedAddress, + chainId: network.chain.id, + }); + returnedResult.isValid = true; + } + } catch { + // do nothing + } + } + setIsLoading(false); + return returnedResult; + }, + [], + ); + return { resolveAddressMultiChain, isLoading }; +}; From 1e8292a5eb7b826e51a446a7eae59da4ba4efa72 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 13 Dec 2024 20:16:38 -0500 Subject: [PATCH 36/48] Refactor DAOSearch to use resolved addresses with chain IDs and update SearchDisplay component --- .../ui/menus/DAOSearch/SearchDisplay.tsx | 7 +- src/components/ui/menus/DAOSearch/index.tsx | 16 ++-- src/hooks/DAO/useSearchDao.ts | 73 +++++++++++++------ 3 files changed, 63 insertions(+), 33 deletions(-) diff --git a/src/components/ui/menus/DAOSearch/SearchDisplay.tsx b/src/components/ui/menus/DAOSearch/SearchDisplay.tsx index 3e02346a56..55d47c5690 100644 --- a/src/components/ui/menus/DAOSearch/SearchDisplay.tsx +++ b/src/components/ui/menus/DAOSearch/SearchDisplay.tsx @@ -4,6 +4,7 @@ import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Address } from 'viem'; import { SafeDisplayRow } from '../../../../pages/home/SafeDisplayRow'; +import { getNetworkConfig } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import { ErrorBoundary } from '../../utils/ErrorBoundary'; import { MySafesErrorFallback } from '../../utils/MySafesErrorFallback'; @@ -13,7 +14,7 @@ interface ISearchDisplay { errorMessage: string | undefined; address: Address | undefined; onClickView: Function; - networkPrefix: string; + chainId: number; } export function SearchDisplay({ @@ -21,7 +22,7 @@ export function SearchDisplay({ errorMessage, address, onClickView, - networkPrefix, + chainId, }: ISearchDisplay) { const { t } = useTranslation(['common', 'dashboard']); const node = useDaoInfoStore(); @@ -90,7 +91,7 @@ export function SearchDisplay({ { onClickView(); }} diff --git a/src/components/ui/menus/DAOSearch/index.tsx b/src/components/ui/menus/DAOSearch/index.tsx index 9c11123910..a3dc58a0fe 100644 --- a/src/components/ui/menus/DAOSearch/index.tsx +++ b/src/components/ui/menus/DAOSearch/index.tsx @@ -21,8 +21,7 @@ export function DAOSearch() { const { t } = useTranslation(['dashboard']); const [localInput, setLocalInput] = useState(''); const [typing, setTyping] = useState(false); - const { errorMessage, isLoading, address, setSearchString, safeFoundNetworkPrefixes } = - useSearchDao(); + const { errorMessage, isLoading, setSearchString, resolvedAddressesWithPrefix } = useSearchDao(); const { isOpen, onOpen, onClose } = useDisclosure(); const ref = useRef(null); @@ -65,9 +64,8 @@ export function DAOSearch() { const showResults = useMemo(() => { if (typing) return false; if (isLoading) return true; - const hasMessage = errorMessage !== undefined || address !== undefined; - return hasMessage; - }, [address, errorMessage, typing, isLoading]); + return errorMessage === undefined; + }, [errorMessage, typing, isLoading]); useEffect(() => { if (localInput) { @@ -150,13 +148,13 @@ export function DAOSearch() { w="full" position="absolute" > - {safeFoundNetworkPrefixes.map(networkPrefix => ( + {resolvedAddressesWithPrefix.map(resolved => ( ))} diff --git a/src/hooks/DAO/useSearchDao.ts b/src/hooks/DAO/useSearchDao.ts index c80ba5d126..2974f98c94 100644 --- a/src/hooks/DAO/useSearchDao.ts +++ b/src/hooks/DAO/useSearchDao.ts @@ -1,39 +1,70 @@ -import { useState, useEffect } from 'react'; +import SafeApiKit from '@safe-global/api-kit'; +import { useState, useEffect, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; -import { useIsSafe } from '../safe/useIsSafe'; -import useAddress from '../utils/useAddress'; +import { Address } from 'viem'; +import { useResolveAddressMultiChain } from '../utils/useResolveAddressMultiChain'; +type ResolvedAddressWithPrefix = { + address: Address; + chainId: number; +}; export const useSearchDao = () => { + const { resolveAddressMultiChain, isLoading: isAddressLoading } = useResolveAddressMultiChain(); const [searchString, setSearchString] = useState(''); const [errorMessage, setErrorMessage] = useState(); - // This hook needs to search all supoorted chains for the address - const { address, isValid, isLoading: isAddressLoading } = useAddress(searchString); - const { isSafe, isSafeLoading, safeFoundNetworkPrefixes } = useIsSafe(address); - const { t } = useTranslation('dashboard'); - const { chain } = useNetworkConfigStore(); + const [isSafeLookupLoading, setIsSafeLookupLoading] = useState(false); + const [resolvedAddressesWithPrefix, setSafeResolvedAddressesWithPrefix] = useState< + ResolvedAddressWithPrefix[] + >([]); + + const findSafes = useCallback( + async (resolvedAddressesWithChainId: { address: Address; chainId: number }[]) => { + setIsSafeLookupLoading(true); + for await (const resolved of resolvedAddressesWithChainId) { + const safeAPI = new SafeApiKit({ chainId: BigInt(resolved.chainId) }); + safeAPI.getSafeCreationInfo(resolved.address); + try { + await safeAPI.getSafeCreationInfo(resolved.address); + + setSafeResolvedAddressesWithPrefix(prevState => [...prevState, resolved]); + } catch (e) { + // Safe not found + continue; + } + } + setIsSafeLookupLoading(false); + }, + [], + ); - const isLoading = isAddressLoading === true || isSafeLoading === true; + const resolveInput = useCallback( + async (input: string) => { + const { resolved, isValid } = await resolveAddressMultiChain(input); + if (isValid) { + await findSafes(resolved); + } else { + setErrorMessage('Invalid search'); + } + }, + [findSafes, resolveAddressMultiChain], + ); + + const { t } = useTranslation('dashboard'); useEffect(() => { setErrorMessage(undefined); - - if (searchString === '' || isLoading || isSafe || isValid === undefined) { + setSafeResolvedAddressesWithPrefix([]); + if (searchString === '') { return; } - if (isValid === true) { - setErrorMessage(t('errorFailedSearch')); - } else { - setErrorMessage(t('errorInvalidSearch')); - } - }, [chain.name, isLoading, isSafe, isValid, searchString, t]); + resolveInput(searchString).catch(() => setErrorMessage(t('errorInvalidSearch'))); + }, [resolveInput, searchString, t]); return { - safeFoundNetworkPrefixes, + resolvedAddressesWithPrefix, errorMessage, - isLoading, - address, + isLoading: isAddressLoading || isSafeLookupLoading, setSearchString, searchString, }; From a25fd55f8c9c9c588cbbe16b76ca6fcc60b6d2f5 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 13 Dec 2024 20:24:16 -0500 Subject: [PATCH 37/48] move translation initialization --- src/hooks/DAO/useSearchDao.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hooks/DAO/useSearchDao.ts b/src/hooks/DAO/useSearchDao.ts index 2974f98c94..5b46fda4c8 100644 --- a/src/hooks/DAO/useSearchDao.ts +++ b/src/hooks/DAO/useSearchDao.ts @@ -9,6 +9,7 @@ type ResolvedAddressWithPrefix = { chainId: number; }; export const useSearchDao = () => { + const { t } = useTranslation('dashboard'); const { resolveAddressMultiChain, isLoading: isAddressLoading } = useResolveAddressMultiChain(); const [searchString, setSearchString] = useState(''); const [errorMessage, setErrorMessage] = useState(); @@ -50,8 +51,6 @@ export const useSearchDao = () => { [findSafes, resolveAddressMultiChain], ); - const { t } = useTranslation('dashboard'); - useEffect(() => { setErrorMessage(undefined); setSafeResolvedAddressesWithPrefix([]); From 8dace81020110f03671b84db5136967cc8e0dca6 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 13 Dec 2024 20:41:11 -0500 Subject: [PATCH 38/48] Remove 'wrongNetwork' type from LoadingProblem component and its usage in SafeController --- src/pages/LoadingProblem.tsx | 2 +- src/pages/dao/SafeController.tsx | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/LoadingProblem.tsx b/src/pages/LoadingProblem.tsx index f755972745..1f45e6b783 100644 --- a/src/pages/LoadingProblem.tsx +++ b/src/pages/LoadingProblem.tsx @@ -7,7 +7,7 @@ import { import { CONTENT_MAXW } from '../constants/common'; import { useNetworkConfigStore } from '../providers/NetworkConfig/useNetworkConfigStore'; -function LoadingProblem({ type }: { type: 'invalidSafe' | 'wrongNetwork' | 'badQueryParam' }) { +function LoadingProblem({ type }: { type: 'invalidSafe' | 'badQueryParam' }) { const { chain } = useNetworkConfigStore(); const { t } = useTranslation('common'); diff --git a/src/pages/dao/SafeController.tsx b/src/pages/dao/SafeController.tsx index f1a23f91d9..76b142c25f 100644 --- a/src/pages/dao/SafeController.tsx +++ b/src/pages/dao/SafeController.tsx @@ -49,8 +49,6 @@ export function SafeController() { // the order of the if blocks of these next three error states matters if (invalidQuery) { return ; - } else if (wrongNetwork) { - return ; } else if (errorLoading) { return ; } From d4aff40bc96494dad69e4ee8eaa37b3a55931cce Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 13 Dec 2024 20:47:34 -0500 Subject: [PATCH 39/48] resetHats store when safeAddress changes --- src/hooks/DAO/useKeyValuePairs.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/hooks/DAO/useKeyValuePairs.ts b/src/hooks/DAO/useKeyValuePairs.ts index e850e1bf4e..6cb4fbdfe8 100644 --- a/src/hooks/DAO/useKeyValuePairs.ts +++ b/src/hooks/DAO/useKeyValuePairs.ts @@ -97,7 +97,7 @@ const useKeyValuePairs = () => { chain, contracts: { keyValuePairs, sablierV2LockupLinear }, } = useNetworkConfig(); - const { setHatKeyValuePairData } = useRolesStore(); + const { setHatKeyValuePairData, resetHatsStore } = useRolesStore(); const safeAddress = node.safe?.address; @@ -159,6 +159,14 @@ const useKeyValuePairs = () => { setHatKeyValuePairData, sablierV2LockupLinear, ]); + + useEffect(() => { + if (!safeAddress) { + return; + } + + resetHatsStore(); + }, [resetHatsStore, safeAddress]); }; export { useKeyValuePairs }; From 4e673cbcf5e279ad39e47605b9bd57f9034554ce Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 13 Dec 2024 20:49:59 -0500 Subject: [PATCH 40/48] remove role store reset, since its not in useKeyValuePairs --- src/hooks/DAO/loaders/useHatsTree.ts | 9 +-------- src/pages/dao/SafeController.tsx | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/hooks/DAO/loaders/useHatsTree.ts b/src/hooks/DAO/loaders/useHatsTree.ts index 168f96a4c6..17727b780e 100644 --- a/src/hooks/DAO/loaders/useHatsTree.ts +++ b/src/hooks/DAO/loaders/useHatsTree.ts @@ -19,7 +19,7 @@ import { getValue, setValue } from '../../utils/cache/useLocalStorage'; const hatsSubgraphClient = new HatsSubgraphClient({}); -const useHatsTree = ({ safeAddress }: { safeAddress: Address | undefined }) => { +const useHatsTree = () => { const { t } = useTranslation('roles'); const { governanceContracts: { @@ -34,7 +34,6 @@ const useHatsTree = ({ safeAddress }: { safeAddress: Address | undefined }) => { streamsFetched, setHatsTree, updateRolesWithStreams, - resetHatsStore, } = useRolesStore(); const ipfsClient = useIPFSClient(); @@ -297,12 +296,6 @@ const useHatsTree = ({ safeAddress }: { safeAddress: Address | undefined }) => { getHatsStreams(); }, [hatsTree, updateRolesWithStreams, getPaymentStreams, streamsFetched]); - - useEffect(() => { - if (safeAddress === undefined) { - resetHatsStore(); - } - }, [resetHatsStore, safeAddress]); }; export { useHatsTree }; diff --git a/src/pages/dao/SafeController.tsx b/src/pages/dao/SafeController.tsx index 6b6ac3c997..2f3c92f688 100644 --- a/src/pages/dao/SafeController.tsx +++ b/src/pages/dao/SafeController.tsx @@ -43,7 +43,7 @@ export function SafeController() { useAzoriusListeners(); useKeyValuePairs(); - useHatsTree({ safeAddress }); + useHatsTree(); // the order of the if blocks of these next three error states matters if (invalidQuery) { From cb0e07f61235fad1d3353b65645118a2d6c49a33 Mon Sep 17 00:00:00 2001 From: Adam Gall Date: Mon, 16 Dec 2024 15:19:37 -0500 Subject: [PATCH 41/48] We only really need to search for ENS names on mainnet --- src/components/ui/page/Global/index.tsx | 18 +------------ src/hooks/utils/useGetSafeName.ts | 34 ++++++++++++------------- 2 files changed, 17 insertions(+), 35 deletions(-) diff --git a/src/components/ui/page/Global/index.tsx b/src/components/ui/page/Global/index.tsx index 21e1be827a..ae4fef745e 100644 --- a/src/components/ui/page/Global/index.tsx +++ b/src/components/ui/page/Global/index.tsx @@ -1,7 +1,6 @@ import * as amplitude from '@amplitude/analytics-browser'; import * as Sentry from '@sentry/react'; import { useEffect, useState } from 'react'; -import { createPublicClient, http, PublicClient } from 'viem'; import { useAccount } from 'wagmi'; import { useAccountFavorites } from '../../../../hooks/DAO/loaders/useFavorites'; import { @@ -36,8 +35,6 @@ const useUpdateFavoritesCache = (onFavoritesUpdated: () => void) => { useEffect(() => { (async () => { - const publicClientsByChain = new Map(); - const favoriteNames = await Promise.all( favoritesList.map(async favorite => { const favoriteChain = wagmiConfig.chains.find( @@ -54,22 +51,9 @@ const useUpdateFavoritesCache = (onFavoritesUpdated: () => void) => { return; } - let favoritePublicClient = publicClientsByChain.get(favoriteChain.id); - - if (!favoritePublicClient) { - favoritePublicClient = createPublicClient({ - chain: favoriteChain, - transport: http(favoriteNetwork.rpcEndpoint), - }); - publicClientsByChain.set(favoriteChain.id, favoritePublicClient); - } - const networkConfig = getNetworkConfig(favoriteChain.id); - return Promise.all([ - favorite, - getSafeName(favoritePublicClient, networkConfig.subgraph, favorite.address), - ]); + return Promise.all([favorite, getSafeName(networkConfig.subgraph, favorite.address)]); }), ); diff --git a/src/hooks/utils/useGetSafeName.ts b/src/hooks/utils/useGetSafeName.ts index 89652c3dfe..356c0e7201 100644 --- a/src/hooks/utils/useGetSafeName.ts +++ b/src/hooks/utils/useGetSafeName.ts @@ -1,25 +1,27 @@ import { useCallback } from 'react'; -import { Address, ChainDoesNotSupportContract, PublicClient } from 'viem'; -import { usePublicClient } from 'wagmi'; +import { Address, http, createPublicClient } from 'viem'; import { DAOQueryDocument } from '../../../.graphclient'; import graphQLClient from '../../graphql'; -import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; +import { + supportedNetworks, + useNetworkConfigStore, +} from '../../providers/NetworkConfig/useNetworkConfigStore'; import { createAccountSubstring } from './useGetAccountName'; export const getSafeName = async ( - publicClient: PublicClient, subgraph: { space: number; slug: string; version: string }, address: Address, ) => { - const ensName = await publicClient.getEnsName({ address }).catch((error: Error) => { - if (error.name === ChainDoesNotSupportContract.name) { - // Sliently fail, this is fine. - // https://github.com/wevm/viem/discussions/781 - } else { - throw error; - } - }); + const mainnet = supportedNetworks.find(network => network.chain.id === 1); + if (!mainnet) { + throw new Error('Mainnet not found'); + } + const mainnetPublicClient = createPublicClient({ + chain: mainnet.chain, + transport: http(mainnet.rpcEndpoint), + }); + const ensName = await mainnetPublicClient.getEnsName({ address }); if (ensName) { return ensName; } @@ -45,17 +47,13 @@ export const getSafeName = async ( export const useGetSafeName = (chainId?: number) => { const { getConfigByChainId } = useNetworkConfigStore(); - const publicClient = usePublicClient({ chainId }); return { getSafeName: useCallback( (address: Address) => { - if (!publicClient) { - throw new Error('Public client not available'); - } - return getSafeName(publicClient, getConfigByChainId(chainId).subgraph, address); + return getSafeName(getConfigByChainId(chainId).subgraph, address); }, - [publicClient, getConfigByChainId, chainId], + [chainId, getConfigByChainId], ), }; }; From c44efe83b2ad1e117dc43d42dd245f79dc918a7a Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:32:50 -0500 Subject: [PATCH 42/48] Refactor useResolveAddressMultiChain to utilize mainnet client for ENS address resolution --- src/hooks/utils/useResolveAddressMultiChain.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/hooks/utils/useResolveAddressMultiChain.ts b/src/hooks/utils/useResolveAddressMultiChain.ts index f3926b1e53..a440033b92 100644 --- a/src/hooks/utils/useResolveAddressMultiChain.ts +++ b/src/hooks/utils/useResolveAddressMultiChain.ts @@ -45,13 +45,18 @@ export const useResolveAddressMultiChain = () => { setIsLoading(false); return returnedResult; } + const mainnet = supportedNetworks.find(network => network.chain.id === 1); + if (!mainnet) { + throw new Error('Mainnet not found'); + } + + const mainnetPublicClient = createPublicClient({ + chain: mainnet.chain, + transport: http(mainnet.rpcEndpoint), + }); for (const network of supportedNetworks) { - const client = createPublicClient({ - chain: network.chain, - transport: http(network.rpcEndpoint), - }); try { - const resolvedAddress = await client.getEnsAddress({ name: normalizedName }); + const resolvedAddress = await mainnetPublicClient.getEnsAddress({ name: normalizedName }); if (resolvedAddress) { returnedResult.resolved.push({ address: resolvedAddress, From 6cdc66ea7f602c07cb77d28d0b40c9222b39f717 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:47:14 -0500 Subject: [PATCH 43/48] Rename useResolveAddressMultiChain to useResolveENSName and refactor for single address resolution --- ...ressMultiChain.ts => useResolveENSName.ts} | 41 +++++++------------ 1 file changed, 15 insertions(+), 26 deletions(-) rename src/hooks/utils/{useResolveAddressMultiChain.ts => useResolveENSName.ts} (61%) diff --git a/src/hooks/utils/useResolveAddressMultiChain.ts b/src/hooks/utils/useResolveENSName.ts similarity index 61% rename from src/hooks/utils/useResolveAddressMultiChain.ts rename to src/hooks/utils/useResolveENSName.ts index a440033b92..7505cec05b 100644 --- a/src/hooks/utils/useResolveAddressMultiChain.ts +++ b/src/hooks/utils/useResolveENSName.ts @@ -1,24 +1,21 @@ import { useState, useCallback } from 'react'; -import { Address, createPublicClient, http, isAddress, getAddress } from 'viem'; +import { Address, createPublicClient, http, isAddress, getAddress, zeroAddress } from 'viem'; import { normalize } from 'viem/ens'; import { supportedNetworks } from '../../providers/NetworkConfig/useNetworkConfigStore'; -type ResolveAddressReturnType = { - resolved: { - address: Address; - chainId: number; - }[]; +type ResolveENSNameReturnType = { + resolvedAddress: Address; isValid: boolean; }; -export const useResolveAddressMultiChain = () => { +export const useResolveENSName = () => { const [isLoading, setIsLoading] = useState(false); const resolveAddressMultiChain = useCallback( - async (input: string): Promise => { + async (input: string): Promise => { setIsLoading(true); - const returnedResult: ResolveAddressReturnType = { - resolved: [], + const returnedResult: ResolveENSNameReturnType = { + resolvedAddress: zeroAddress, isValid: false, }; @@ -29,10 +26,7 @@ export const useResolveAddressMultiChain = () => { if (isAddress(input)) { // @dev if its a valid address, its valid on all networks returnedResult.isValid = true; - returnedResult.resolved = supportedNetworks.map(network => ({ - address: getAddress(input), - chainId: network.chain.id, - })); + returnedResult.resolvedAddress = getAddress(input); setIsLoading(false); return returnedResult; } @@ -54,19 +48,14 @@ export const useResolveAddressMultiChain = () => { chain: mainnet.chain, transport: http(mainnet.rpcEndpoint), }); - for (const network of supportedNetworks) { - try { - const resolvedAddress = await mainnetPublicClient.getEnsAddress({ name: normalizedName }); - if (resolvedAddress) { - returnedResult.resolved.push({ - address: resolvedAddress, - chainId: network.chain.id, - }); - returnedResult.isValid = true; - } - } catch { - // do nothing + try { + const resolvedAddress = await mainnetPublicClient.getEnsAddress({ name: normalizedName }); + if (resolvedAddress) { + returnedResult.resolvedAddress = resolvedAddress; + returnedResult.isValid = true; } + } catch { + // do nothing } setIsLoading(false); return returnedResult; From 8fc85e2ffcf48f1fd9885b5e7346f43a6663505f Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:47:18 -0500 Subject: [PATCH 44/48] Refactor useSearchDao to utilize useResolveENSName for address resolution and update resolved address handling --- src/hooks/DAO/useSearchDao.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/hooks/DAO/useSearchDao.ts b/src/hooks/DAO/useSearchDao.ts index 5b46fda4c8..8664bc5703 100644 --- a/src/hooks/DAO/useSearchDao.ts +++ b/src/hooks/DAO/useSearchDao.ts @@ -2,7 +2,8 @@ import SafeApiKit from '@safe-global/api-kit'; import { useState, useEffect, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { Address } from 'viem'; -import { useResolveAddressMultiChain } from '../utils/useResolveAddressMultiChain'; +import { supportedNetworks } from '../../providers/NetworkConfig/useNetworkConfigStore'; +import { useResolveENSName } from '../utils/useResolveENSName'; type ResolvedAddressWithPrefix = { address: Address; @@ -10,7 +11,7 @@ type ResolvedAddressWithPrefix = { }; export const useSearchDao = () => { const { t } = useTranslation('dashboard'); - const { resolveAddressMultiChain, isLoading: isAddressLoading } = useResolveAddressMultiChain(); + const { resolveAddressMultiChain, isLoading: isAddressLoading } = useResolveENSName(); const [searchString, setSearchString] = useState(''); const [errorMessage, setErrorMessage] = useState(); @@ -41,9 +42,14 @@ export const useSearchDao = () => { const resolveInput = useCallback( async (input: string) => { - const { resolved, isValid } = await resolveAddressMultiChain(input); + const { resolvedAddress, isValid } = await resolveAddressMultiChain(input); if (isValid) { - await findSafes(resolved); + await findSafes( + supportedNetworks.map(network => ({ + address: resolvedAddress, + chainId: network.chain.id, + })), + ); } else { setErrorMessage('Invalid search'); } From 98d5e0ab275c1db28e6c5f4c7b39aaff3bfe6f1e Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:54:16 -0500 Subject: [PATCH 45/48] Refactor useSearchDao to use resolveENSName for address resolution and update related logic --- src/hooks/DAO/useSearchDao.ts | 6 +- src/hooks/utils/useResolveENSName.ts | 91 ++++++++++++++-------------- 2 files changed, 47 insertions(+), 50 deletions(-) diff --git a/src/hooks/DAO/useSearchDao.ts b/src/hooks/DAO/useSearchDao.ts index 8664bc5703..69c2331194 100644 --- a/src/hooks/DAO/useSearchDao.ts +++ b/src/hooks/DAO/useSearchDao.ts @@ -11,7 +11,7 @@ type ResolvedAddressWithPrefix = { }; export const useSearchDao = () => { const { t } = useTranslation('dashboard'); - const { resolveAddressMultiChain, isLoading: isAddressLoading } = useResolveENSName(); + const { resolveENSName, isLoading: isAddressLoading } = useResolveENSName(); const [searchString, setSearchString] = useState(''); const [errorMessage, setErrorMessage] = useState(); @@ -42,7 +42,7 @@ export const useSearchDao = () => { const resolveInput = useCallback( async (input: string) => { - const { resolvedAddress, isValid } = await resolveAddressMultiChain(input); + const { resolvedAddress, isValid } = await resolveENSName(input); if (isValid) { await findSafes( supportedNetworks.map(network => ({ @@ -54,7 +54,7 @@ export const useSearchDao = () => { setErrorMessage('Invalid search'); } }, - [findSafes, resolveAddressMultiChain], + [findSafes, resolveENSName], ); useEffect(() => { diff --git a/src/hooks/utils/useResolveENSName.ts b/src/hooks/utils/useResolveENSName.ts index 7505cec05b..e4df9cdc7c 100644 --- a/src/hooks/utils/useResolveENSName.ts +++ b/src/hooks/utils/useResolveENSName.ts @@ -10,57 +10,54 @@ type ResolveENSNameReturnType = { export const useResolveENSName = () => { const [isLoading, setIsLoading] = useState(false); - const resolveAddressMultiChain = useCallback( - async (input: string): Promise => { - setIsLoading(true); + const resolveENSName = useCallback(async (input: string): Promise => { + setIsLoading(true); - const returnedResult: ResolveENSNameReturnType = { - resolvedAddress: zeroAddress, - isValid: false, - }; + const returnedResult: ResolveENSNameReturnType = { + resolvedAddress: zeroAddress, + isValid: false, + }; - if (input === '') { - throw new Error('ENS name is empty'); - } - - if (isAddress(input)) { - // @dev if its a valid address, its valid on all networks - returnedResult.isValid = true; - returnedResult.resolvedAddress = getAddress(input); - setIsLoading(false); - return returnedResult; - } + if (input === '') { + throw new Error('ENS name is empty'); + } - // @dev if its not an address, try to resolve as possible ENS name on all networks - let normalizedName: string; - try { - normalizedName = normalize(input); - } catch { - setIsLoading(false); - return returnedResult; - } - const mainnet = supportedNetworks.find(network => network.chain.id === 1); - if (!mainnet) { - throw new Error('Mainnet not found'); - } + if (isAddress(input)) { + // @dev if its a valid address, its valid on all networks + returnedResult.isValid = true; + returnedResult.resolvedAddress = getAddress(input); + setIsLoading(false); + return returnedResult; + } - const mainnetPublicClient = createPublicClient({ - chain: mainnet.chain, - transport: http(mainnet.rpcEndpoint), - }); - try { - const resolvedAddress = await mainnetPublicClient.getEnsAddress({ name: normalizedName }); - if (resolvedAddress) { - returnedResult.resolvedAddress = resolvedAddress; - returnedResult.isValid = true; - } - } catch { - // do nothing - } + // @dev if its not an address, try to resolve as possible ENS name on all networks + let normalizedName: string; + try { + normalizedName = normalize(input); + } catch { setIsLoading(false); return returnedResult; - }, - [], - ); - return { resolveAddressMultiChain, isLoading }; + } + const mainnet = supportedNetworks.find(network => network.chain.id === 1); + if (!mainnet) { + throw new Error('Mainnet not found'); + } + + const mainnetPublicClient = createPublicClient({ + chain: mainnet.chain, + transport: http(mainnet.rpcEndpoint), + }); + try { + const resolvedAddress = await mainnetPublicClient.getEnsAddress({ name: normalizedName }); + if (resolvedAddress) { + returnedResult.resolvedAddress = resolvedAddress; + returnedResult.isValid = true; + } + } catch { + // do nothing + } + setIsLoading(false); + return returnedResult; + }, []); + return { resolveENSName, isLoading }; }; From 30b14dc7fcd66ca4d54f08b7b3fa457ab370c4d5 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:18:35 -0500 Subject: [PATCH 46/48] remove try/catch --- src/hooks/utils/useResolveENSName.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/hooks/utils/useResolveENSName.ts b/src/hooks/utils/useResolveENSName.ts index e4df9cdc7c..7ba84734b9 100644 --- a/src/hooks/utils/useResolveENSName.ts +++ b/src/hooks/utils/useResolveENSName.ts @@ -47,15 +47,12 @@ export const useResolveENSName = () => { chain: mainnet.chain, transport: http(mainnet.rpcEndpoint), }); - try { - const resolvedAddress = await mainnetPublicClient.getEnsAddress({ name: normalizedName }); - if (resolvedAddress) { - returnedResult.resolvedAddress = resolvedAddress; - returnedResult.isValid = true; - } - } catch { - // do nothing + const resolvedAddress = await mainnetPublicClient.getEnsAddress({ name: normalizedName }); + if (resolvedAddress) { + returnedResult.resolvedAddress = resolvedAddress; + returnedResult.isValid = true; } + setIsLoading(false); return returnedResult; }, []); From 3a6d01ed48397f63e7897f1793f809a219275bbb Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:47:35 -0500 Subject: [PATCH 47/48] wait for wallet to be fetch before trying to switch chain --- src/hooks/utils/useAutomaticSwitchChain.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/hooks/utils/useAutomaticSwitchChain.ts b/src/hooks/utils/useAutomaticSwitchChain.ts index ec7cd394a2..7d6148f5fc 100644 --- a/src/hooks/utils/useAutomaticSwitchChain.ts +++ b/src/hooks/utils/useAutomaticSwitchChain.ts @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { useSwitchChain } from 'wagmi'; +import { useSwitchChain, useWalletClient } from 'wagmi'; import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { getChainIdFromPrefix } from '../../utils/url'; @@ -9,6 +9,7 @@ export const useAutomaticSwitchChain = ({ urlAddressPrefix: string | undefined; }) => { const { setCurrentConfig, getConfigByChainId, addressPrefix } = useNetworkConfigStore(); + const { isFetched } = useWalletClient(); const { switchChain } = useSwitchChain({ mutation: { onError: () => { @@ -25,9 +26,16 @@ export const useAutomaticSwitchChain = ({ return; } const chainId = getChainIdFromPrefix(urlAddressPrefix); - if (addressPrefix !== urlAddressPrefix && urlAddressPrefix !== undefined) { + if (addressPrefix !== urlAddressPrefix && urlAddressPrefix !== undefined && isFetched) { switchChain({ chainId }); } setTimeout(() => setCurrentConfig(getConfigByChainId(chainId)), 300); - }, [addressPrefix, setCurrentConfig, getConfigByChainId, urlAddressPrefix, switchChain]); + }, [ + addressPrefix, + setCurrentConfig, + getConfigByChainId, + urlAddressPrefix, + switchChain, + isFetched, + ]); }; From 63c1575f8664ae5086fe9eba558417f8d9932858 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Tue, 17 Dec 2024 13:19:55 -0500 Subject: [PATCH 48/48] refactor: update useWalletClient to use isFetchedAfterMount for chain switching logic --- src/hooks/utils/useAutomaticSwitchChain.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/hooks/utils/useAutomaticSwitchChain.ts b/src/hooks/utils/useAutomaticSwitchChain.ts index 7d6148f5fc..7fc2f36a48 100644 --- a/src/hooks/utils/useAutomaticSwitchChain.ts +++ b/src/hooks/utils/useAutomaticSwitchChain.ts @@ -9,7 +9,7 @@ export const useAutomaticSwitchChain = ({ urlAddressPrefix: string | undefined; }) => { const { setCurrentConfig, getConfigByChainId, addressPrefix } = useNetworkConfigStore(); - const { isFetched } = useWalletClient(); + const { isFetchedAfterMount } = useWalletClient(); const { switchChain } = useSwitchChain({ mutation: { onError: () => { @@ -26,7 +26,11 @@ export const useAutomaticSwitchChain = ({ return; } const chainId = getChainIdFromPrefix(urlAddressPrefix); - if (addressPrefix !== urlAddressPrefix && urlAddressPrefix !== undefined && isFetched) { + if ( + addressPrefix !== urlAddressPrefix && + urlAddressPrefix !== undefined && + isFetchedAfterMount + ) { switchChain({ chainId }); } setTimeout(() => setCurrentConfig(getConfigByChainId(chainId)), 300); @@ -36,6 +40,6 @@ export const useAutomaticSwitchChain = ({ getConfigByChainId, urlAddressPrefix, switchChain, - isFetched, + isFetchedAfterMount, ]); };