From 09d31ad701756ca9ec6cf84ac03ed9932282eead Mon Sep 17 00:00:00 2001 From: Adam Gall Date: Thu, 16 Jan 2025 13:19:28 -0500 Subject: [PATCH 01/13] Prototype "wallet client" that automatically switches to the right chain when a transaction triggered --- src/hooks/DAO/useDeployDAO.ts | 17 +++-------- src/useWaletClient.ts | 57 +++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 13 deletions(-) create mode 100644 src/useWaletClient.ts diff --git a/src/hooks/DAO/useDeployDAO.ts b/src/hooks/DAO/useDeployDAO.ts index 664356ab4..e42b5fab2 100644 --- a/src/hooks/DAO/useDeployDAO.ts +++ b/src/hooks/DAO/useDeployDAO.ts @@ -1,10 +1,10 @@ import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { Address, getContract, isHex } from 'viem'; -import { useWalletClient } from 'wagmi'; import MultiSendCallOnlyAbi from '../../assets/abi/MultiSendCallOnly'; import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; -import { SafeMultisigDAO, AzoriusERC20DAO, AzoriusERC721DAO } from '../../types'; +import { AzoriusERC20DAO, AzoriusERC721DAO, SafeMultisigDAO } from '../../types'; +import { useWaletClient } from '../../useWaletClient'; import { useTransaction } from '../utils/useTransaction'; import useBuildDAOTx from './useBuildDAOTx'; @@ -20,7 +20,7 @@ const useDeployDAO = () => { contracts: { multiSendCallOnly }, } = useNetworkConfigStore(); - const { data: walletClient } = useWalletClient({ + const { data: walletClient } = useWaletClient({ chainId: chain.id, }); @@ -34,15 +34,6 @@ const useDeployDAO = () => { return; } - // ensure the chain is correct - await walletClient.switchChain({ - id: chain.id, - }); - - if (walletClient.chain.id !== chain.id) { - throw new Error('wallet client chain does not match network config'); - } - const builtSafeTx = await build(daoData); if (!builtSafeTx) { return; @@ -72,7 +63,7 @@ const useDeployDAO = () => { deploy(); }, - [addressPrefix, build, chain.id, contractCall, multiSendCallOnly, t, walletClient], + [addressPrefix, build, contractCall, multiSendCallOnly, t, walletClient], ); return [deployDao, pending] as const; diff --git a/src/useWaletClient.ts b/src/useWaletClient.ts new file mode 100644 index 000000000..c335f2555 --- /dev/null +++ b/src/useWaletClient.ts @@ -0,0 +1,57 @@ +import { + Account, + Chain, + Hash, + SendTransactionParameters, + WriteContractParameters, + Abi, + ContractFunctionName, + ContractFunctionArgs, + SendTransactionRequest, +} from 'viem'; +import { useWalletClient } from 'wagmi'; + +type WalletClientExtension = { + sendTransaction< + TRequest extends SendTransactionRequest, + TChainOverride extends Chain | undefined = undefined, + >( + args: SendTransactionParameters, + ): Promise; + writeContract< + TAbi extends Abi | readonly unknown[], + TFunctionName extends ContractFunctionName, + TArgs extends ContractFunctionArgs, + TChain extends Chain | undefined = Chain, + >( + args: WriteContractParameters, + ): Promise; +}; + +export const useWaletClient = ({ chainId }: { chainId: number }) => { + const walletClient = useWalletClient({ chainId }); + + const extendedWalletClient = walletClient.data?.extend( + (client): WalletClientExtension => ({ + async sendTransaction< + TRequest extends SendTransactionRequest, + TChainOverride extends Chain | undefined = undefined, + >(args: SendTransactionParameters) { + await client.switchChain({ id: chainId }); + return client.sendTransaction(args); + }, + + async writeContract< + TAbi extends Abi | readonly unknown[], + TFunctionName extends ContractFunctionName, + TArgs extends ContractFunctionArgs, + TChain extends Chain | undefined = Chain, + >(args: WriteContractParameters) { + await client.switchChain({ id: chainId }); + return client.writeContract(args); + }, + }), + ); + + return { ...walletClient, data: extendedWalletClient }; +}; From db742806a53547b1ad7cdc4a7d9bfed871254b41 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:12:36 -0500 Subject: [PATCH 02/13] Optimize useNetworkPublicClient hook by memoizing the public client creation --- src/hooks/useNetworkPublicClient.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/hooks/useNetworkPublicClient.ts b/src/hooks/useNetworkPublicClient.ts index 9287bd618..8f553f52a 100644 --- a/src/hooks/useNetworkPublicClient.ts +++ b/src/hooks/useNetworkPublicClient.ts @@ -1,13 +1,19 @@ +import { useMemo } from 'react'; import { createPublicClient, http } from 'viem'; import { useNetworkConfigStore } from '../providers/NetworkConfig/useNetworkConfigStore'; export default function useNetworkPublicClient() { const { chain, rpcEndpoint } = useNetworkConfigStore(); - return createPublicClient({ - chain, - batch: { - multicall: true, - }, - transport: http(rpcEndpoint), - }); + const publicClient = useMemo( + () => + createPublicClient({ + chain, + batch: { + multicall: true, + }, + transport: http(rpcEndpoint), + }), + [chain, rpcEndpoint], + ); + return publicClient; } From 5596497cdb72009dddbe80248f7f3bd06ff2b8e0 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:14:07 -0500 Subject: [PATCH 03/13] Refactor hooks to use useNetworkPublicClient instead of usePublicClient - removes public !== undefined conditional as its no longer a thing --- .../hooks/useTreasuryLidoInteractions.ts | 6 ++--- src/components/DaoDashboard/ERC20Claim.tsx | 7 +++--- .../DaoDashboard/Info/InfoGovernance.tsx | 13 +++++----- .../DaoHierarchy/DaoHierarchyNode.tsx | 14 +++-------- src/components/Proposals/ProposalSummary.tsx | 9 ++++--- .../ProposalVotes/context/VoteContext.tsx | 9 ++++--- src/components/Roles/RolePaymentDetails.tsx | 13 +++------- src/components/Roles/forms/RoleFormTerms.tsx | 7 ++---- .../Signers/modals/AddSignerModal.tsx | 6 ++--- src/components/ui/forms/ABISelector.tsx | 7 +++--- src/components/ui/modals/DelegateModal.tsx | 9 ++++--- .../ui/modals/ForkProposalTemplateModal.tsx | 6 ++--- src/components/ui/modals/SendAssetsModal.tsx | 6 ++--- .../ui/proposal/useProposalCountdown.tsx | 6 ++--- .../loaders/governance/useAzoriusListeners.ts | 17 ++++++------- .../loaders/governance/useAzoriusProposals.ts | 16 ++++-------- .../DAO/loaders/governance/useERC20Claim.ts | 6 ++--- .../governance/useERC20LinearStrategy.ts | 8 +++--- .../loaders/governance/useERC20LinearToken.ts | 15 +++++------ .../governance/useERC721LinearStrategy.ts | 8 +++--- .../DAO/loaders/governance/useERC721Tokens.ts | 6 ++--- .../DAO/loaders/governance/useLockRelease.ts | 19 +++++++------- src/hooks/DAO/loaders/useDecentModules.ts | 8 +++--- src/hooks/DAO/loaders/useDecentTreasury.ts | 7 ++---- src/hooks/DAO/loaders/useFractalFreeze.ts | 10 +++----- .../DAO/loaders/useFractalGuardContracts.ts | 8 ++---- .../DAO/loaders/useGovernanceContracts.ts | 8 ++---- src/hooks/DAO/loaders/useHatsTree.ts | 5 ++-- .../DAO/proposal/useCreateProposalTemplate.ts | 6 ++--- src/hooks/DAO/proposal/usePrepareProposal.ts | 6 ++--- src/hooks/DAO/proposal/useSubmitProposal.ts | 9 ++++--- .../DAO/proposal/useUpdateProposalState.ts | 6 ++--- .../DAO/proposal/useUserERC721VotingTokens.ts | 7 +++--- src/hooks/DAO/useDeployAzorius.ts | 6 ++--- src/hooks/DAO/useKeyValuePairs.ts | 6 ++--- src/hooks/utils/useAddress.ts | 13 +++------- src/hooks/utils/useAddressContractType.ts | 8 ++---- src/hooks/utils/useBlockTimestamp.ts | 6 ++--- src/hooks/utils/useCanUserSubmitProposal.ts | 7 +++--- src/hooks/utils/useCreateRoles.ts | 25 +++---------------- src/hooks/utils/useCurrentBlockNumber.ts | 9 ++----- src/hooks/utils/useSafeDecoder.tsx | 7 ++---- src/hooks/utils/useSafeTransactions.ts | 10 ++++---- .../utils/useVotingStrategiesAddresses.ts | 6 ++--- .../sablier/SafeSablierProposalCreatePage.tsx | 6 ++--- .../SafePermissionsCreateProposal.tsx | 6 ++--- 46 files changed, 170 insertions(+), 238 deletions(-) diff --git a/src/components/DAOTreasury/hooks/useTreasuryLidoInteractions.ts b/src/components/DAOTreasury/hooks/useTreasuryLidoInteractions.ts index 6521f318c..e6b6326cb 100644 --- a/src/components/DAOTreasury/hooks/useTreasuryLidoInteractions.ts +++ b/src/components/DAOTreasury/hooks/useTreasuryLidoInteractions.ts @@ -1,8 +1,8 @@ import { useEffect, useState } from 'react'; import { getContract } from 'viem'; -import { usePublicClient } from 'wagmi'; import LidoWithdrawalQueueAbi from '../../../assets/abi/LidoWithdrawalQueueAbi'; import useLidoStaking from '../../../hooks/stake/lido/useLidoStaking'; +import useNetworkPublicClient from '../../../hooks/useNetworkPublicClient'; import { useCanUserCreateProposal } from '../../../hooks/utils/useCanUserSubmitProposal'; import { useFractal } from '../../../providers/App/AppProvider'; import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; @@ -17,7 +17,7 @@ export default function useTreasuryLidoInteractions() { const { handleUnstake, handleClaimUnstakedETH } = useLidoStaking(); const { canUserCreateProposal } = useCanUserCreateProposal(); const { staking } = useNetworkConfigStore(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); // --- Lido Stake button setup --- const showStakeButton = @@ -48,7 +48,7 @@ export default function useTreasuryLidoInteractions() { useEffect(() => { const getLidoClaimableStatus = async () => { - if (!staking.lido?.withdrawalQueueContractAddress || !lidoWithdrawalNFT || !publicClient) { + if (!staking.lido?.withdrawalQueueContractAddress || !lidoWithdrawalNFT) { return; } diff --git a/src/components/DaoDashboard/ERC20Claim.tsx b/src/components/DaoDashboard/ERC20Claim.tsx index c01f8ec50..ad2655c0d 100644 --- a/src/components/DaoDashboard/ERC20Claim.tsx +++ b/src/components/DaoDashboard/ERC20Claim.tsx @@ -3,8 +3,9 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { getContract } from 'viem'; -import { useAccount, usePublicClient, useWalletClient } from 'wagmi'; +import { useAccount, useWalletClient } from 'wagmi'; import { Alert as AlertIcon } from '../../assets/theme/custom/icons/Alert'; +import useNetworkPublicClient from '../../hooks/useNetworkPublicClient'; import { useTransaction } from '../../hooks/utils/useTransaction'; import { useFractal } from '../../providers/App/AppProvider'; import { AzoriusGovernance } from '../../types'; @@ -19,11 +20,11 @@ export function ERCO20Claim() { const { t } = useTranslation(['dashboard', 'transaction']); const [contractCall, pending] = useTransaction(); const azoriusGovernance = governance as AzoriusGovernance; - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const { data: walletClient } = useWalletClient(); const loadClaim = useCallback(async () => { - if (!tokenClaimContractAddress || !type || !account || !publicClient) { + if (!tokenClaimContractAddress || !type || !account) { return; } const tokenClaimContract = getContract({ diff --git a/src/components/DaoDashboard/Info/InfoGovernance.tsx b/src/components/DaoDashboard/Info/InfoGovernance.tsx index 9b84ba770..b7301f085 100644 --- a/src/components/DaoDashboard/Info/InfoGovernance.tsx +++ b/src/components/DaoDashboard/Info/InfoGovernance.tsx @@ -4,7 +4,8 @@ import { Bank } from '@phosphor-icons/react'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { getContract } from 'viem'; -import { usePublicClient } from 'wagmi'; + +import useNetworkPublicClient from '../../../hooks/useNetworkPublicClient'; import { useTimeHelpers } from '../../../hooks/utils/useTimeHelpers'; import { useFractal } from '../../../providers/App/AppProvider'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; @@ -19,7 +20,7 @@ export function InfoGovernance({ showTitle = true }: { showTitle?: boolean }) { guardContracts: { freezeGuardType, freezeGuardContractAddress }, } = useFractal(); const { safe } = useDaoInfoStore(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const { getTimeDuration } = useTimeHelpers(); const [timelockPeriod, setTimelockPeriod] = useState(); const [executionPeriod, setExecutionPeriod] = useState(); @@ -28,11 +29,9 @@ export function InfoGovernance({ showTitle = true }: { showTitle?: boolean }) { useEffect(() => { const setTimelockInfo = async () => { - const formatBlocks = async (blocks: number): Promise => { - if (publicClient) { - return getTimeDuration(await blocksToSeconds(blocks, publicClient)); - } - }; + const formatBlocks = async (blocks: number): Promise => + getTimeDuration(await blocksToSeconds(blocks, publicClient)); + if (freezeGuardType == FreezeGuardType.MULTISIG) { if (freezeGuardContractAddress && publicClient) { const freezeGuardContract = getContract({ diff --git a/src/components/DaoHierarchy/DaoHierarchyNode.tsx b/src/components/DaoHierarchy/DaoHierarchyNode.tsx index 6b041fa22..dd686690d 100644 --- a/src/components/DaoHierarchy/DaoHierarchyNode.tsx +++ b/src/components/DaoHierarchy/DaoHierarchyNode.tsx @@ -6,11 +6,11 @@ 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 { usePublicClient } from 'wagmi'; import { DAOQueryDocument } from '../../../.graphclient'; import { SENTINEL_ADDRESS } from '../../constants/common'; import { DAO_ROUTES } from '../../constants/routes'; import { useDecentModules } from '../../hooks/DAO/loaders/useDecentModules'; +import useNetworkPublicClient from '../../hooks/useNetworkPublicClient'; import { CacheKeys } from '../../hooks/utils/cache/cacheDefaults'; import { setValue, getValue } from '../../hooks/utils/cache/useLocalStorage'; import { useAddressContractType } from '../../hooks/utils/useAddressContractType'; @@ -44,7 +44,7 @@ export function DaoHierarchyNode({ const [hierarchyNode, setHierarchyNode] = useState(); const [hasErrorLoading, setErrorLoading] = useState(false); const { addressPrefix, subgraph, chain } = useNetworkConfigStore(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const { getAddressContractType } = useAddressContractType(); const lookupModules = useDecentModules(); @@ -59,10 +59,6 @@ export function DaoHierarchyNode({ const getVotingStrategies = useCallback( async (azoriusModule: DecentModule) => { - if (!publicClient) { - throw new Error('Public Client is not set!'); - } - const azoriusContract = getContract({ abi: abis.Azorius, address: azoriusModule.moduleAddress, @@ -98,10 +94,6 @@ export function DaoHierarchyNode({ throw new Error('No voting strategies found'); } - if (!publicClient) { - throw new Error('Public Client is not set!'); - } - let governanceTypes: DaoHierarchyStrategyType[] = []; await Promise.all( @@ -125,7 +117,7 @@ export function DaoHierarchyNode({ ); return governanceTypes.filter((value, index, self) => self.indexOf(value) === index); }, - [getVotingStrategies, publicClient], + [getVotingStrategies], ); const loadDao = useCallback( diff --git a/src/components/Proposals/ProposalSummary.tsx b/src/components/Proposals/ProposalSummary.tsx index 4a85847c0..3c604d6f0 100644 --- a/src/components/Proposals/ProposalSummary.tsx +++ b/src/components/Proposals/ProposalSummary.tsx @@ -5,8 +5,9 @@ import { formatInTimeZone } from 'date-fns-tz'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { erc721Abi, getContract } from 'viem'; -import { useAccount, usePublicClient } from 'wagmi'; +import { useAccount } from 'wagmi'; import { TOOLTIP_MAXW } from '../../constants/common'; +import useNetworkPublicClient from '../../hooks/useNetworkPublicClient'; import useBlockTimestamp from '../../hooks/utils/useBlockTimestamp'; import { useFractal } from '../../providers/App/AppProvider'; import { AzoriusGovernance, AzoriusProposal, GovernanceType } from '../../types'; @@ -58,10 +59,10 @@ export function AzoriusProposalSummary({ proposal }: { proposal: AzoriusProposal const toggleShowVotingPower = () => setShowVotingPower(prevState => !prevState); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const getErc721VotingWeight = useCallback(async () => { - if (!address || !azoriusGovernance.erc721Tokens || !publicClient) { + if (!address || !azoriusGovernance.erc721Tokens) { return 0n; } const userVotingWeight = ( @@ -88,7 +89,7 @@ export function AzoriusProposalSummary({ proposal }: { proposal: AzoriusProposal useEffect(() => { async function loadProposalVotingWeight() { - if (address && publicClient) { + if (address) { if (isERC20) { const strategyContract = getContract({ abi: abis.LinearERC20Voting, diff --git a/src/components/Proposals/ProposalVotes/context/VoteContext.tsx b/src/components/Proposals/ProposalVotes/context/VoteContext.tsx index fc86b9869..e09c9307f 100644 --- a/src/components/Proposals/ProposalVotes/context/VoteContext.tsx +++ b/src/components/Proposals/ProposalVotes/context/VoteContext.tsx @@ -10,9 +10,10 @@ import { useState, } from 'react'; import { Address, erc721Abi, getContract } from 'viem'; -import { useAccount, usePublicClient } from 'wagmi'; +import { useAccount } from 'wagmi'; import useSnapshotProposal from '../../../../hooks/DAO/loaders/snapshot/useSnapshotProposal'; import useUserERC721VotingTokens from '../../../../hooks/DAO/proposal/useUserERC721VotingTokens'; +import useNetworkPublicClient from '../../../../hooks/useNetworkPublicClient'; import { useFractal } from '../../../../providers/App/AppProvider'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import { @@ -67,7 +68,7 @@ export function VoteContextProvider({ const { loadVotingWeight } = useSnapshotProposal(proposal as SnapshotProposal); const { remainingTokenIds } = useUserERC721VotingTokens(null, proposal.proposalId, true); const { snapshotProposal } = useSnapshotProposal(proposal); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const getHasVoted = useCallback(() => { setHasVotedLoading(true); @@ -101,7 +102,7 @@ export function VoteContextProvider({ const erc721VotingWeight = useCallback(async () => { const account = userAccount.address; const azoriusGovernance = governance as AzoriusGovernance; - if (!account || !azoriusGovernance.erc721Tokens || !publicClient) { + if (!account || !azoriusGovernance.erc721Tokens) { return 0n; } const userVotingWeight = ( @@ -123,7 +124,7 @@ export function VoteContextProvider({ const getCanVote = useCallback(async () => { setCanVoteLoading(true); let newCanVote = false; - if (userAccount.address && publicClient) { + if (userAccount.address) { if (snapshotProposal) { const votingWeightData = await loadVotingWeight(); newCanVote = votingWeightData.votingWeight >= 1; diff --git a/src/components/Roles/RolePaymentDetails.tsx b/src/components/Roles/RolePaymentDetails.tsx index 09c40855f..3fa7dbfa3 100644 --- a/src/components/Roles/RolePaymentDetails.tsx +++ b/src/components/Roles/RolePaymentDetails.tsx @@ -5,9 +5,10 @@ import { TouchEvent, useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { Address, getAddress, Hex } from 'viem'; -import { useAccount, usePublicClient } from 'wagmi'; +import { useAccount } from 'wagmi'; import { DETAILS_BOX_SHADOW } from '../../constants/common'; import { DAO_ROUTES } from '../../constants/routes'; +import useNetworkPublicClient from '../../hooks/useNetworkPublicClient'; import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore'; import { useRolesStore } from '../../store/roles/useRolesStore'; @@ -145,7 +146,7 @@ export function RolePaymentDetails({ const { addressPrefix } = useNetworkConfigStore(); const { refreshWithdrawableAmount } = useRolesStore(); const navigate = useNavigate(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const canWithdraw = useMemo(() => { if ( connectedAccount && @@ -162,13 +163,7 @@ export function RolePaymentDetails({ }, [payment.endDate, roleTerms]); const [modalType, props] = useMemo(() => { - if ( - !payment.streamId || - !payment.contractAddress || - !roleHatWearerAddress || - !publicClient || - !roleHatId - ) { + if (!payment.streamId || !payment.contractAddress || !roleHatWearerAddress || !roleHatId) { return [ModalType.NONE] as const; } let recipient = roleHatWearerAddress; diff --git a/src/components/Roles/forms/RoleFormTerms.tsx b/src/components/Roles/forms/RoleFormTerms.tsx index 5aaf182f0..2b705fb81 100644 --- a/src/components/Roles/forms/RoleFormTerms.tsx +++ b/src/components/Roles/forms/RoleFormTerms.tsx @@ -17,8 +17,8 @@ import { useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { toast } from 'sonner'; import { getAddress, Hex } from 'viem'; -import { usePublicClient } from 'wagmi'; import { DETAILS_BOX_SHADOW } from '../../../constants/common'; +import useNetworkPublicClient from '../../../hooks/useNetworkPublicClient'; import { useRolesStore } from '../../../store/roles/useRolesStore'; import { RoleFormTermStatus, RoleFormValues } from '../../../types/roles'; import { DatePicker } from '../../ui/forms/DatePicker'; @@ -95,15 +95,12 @@ function RoleTermCreate({ }) { const { t } = useTranslation('roles'); const { values, errors, setFieldValue } = useFormikContext(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const handleAddTerm = async () => { if (!values.newRoleTerm?.nominee || !values.newRoleTerm?.termEndDate) { throw new Error('Nominee and Term End Date are required'); } - if (!publicClient) { - throw new Error('Public client is not available'); - } let nomineeAddress = values.newRoleTerm.nominee; if (!values.newRoleTerm.nominee.startsWith('0x') && !getAddress(values.newRoleTerm.nominee)) { const ens = await publicClient.getEnsAddress({ diff --git a/src/components/SafeSettings/Signers/modals/AddSignerModal.tsx b/src/components/SafeSettings/Signers/modals/AddSignerModal.tsx index 438be3b20..842475b25 100644 --- a/src/components/SafeSettings/Signers/modals/AddSignerModal.tsx +++ b/src/components/SafeSettings/Signers/modals/AddSignerModal.tsx @@ -4,9 +4,9 @@ import { Field, FieldAttributes, Formik } from 'formik'; import { useCallback, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { Address, getAddress } from 'viem'; -import { usePublicClient } from 'wagmi'; import * as Yup from 'yup'; import { useValidationAddress } from '../../../../hooks/schemas/common/useValidationAddress'; +import useNetworkPublicClient from '../../../../hooks/useNetworkPublicClient'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; import { validateENSName } from '../../../../utils/url'; import SupportTooltip from '../../../ui/badges/SupportTooltip'; @@ -31,7 +31,7 @@ function AddSignerModal({ } const { t } = useTranslation(['modals', 'common']); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const { addressValidationTest, newSignerValidationTest } = useValidationAddress(); const tooltipContainer = useRef(null); @@ -41,7 +41,7 @@ function AddSignerModal({ async (values: { address: string; threshold: number; nonce: number }) => { const { address, nonce, threshold } = values; let validAddress = address; - if (validateENSName(validAddress) && publicClient) { + if (validateENSName(validAddress)) { const maybeEnsAddress = await publicClient.getEnsAddress({ name: address }); if (maybeEnsAddress) { validAddress = maybeEnsAddress; diff --git a/src/components/ui/forms/ABISelector.tsx b/src/components/ui/forms/ABISelector.tsx index 62272903e..07f1f6dc9 100644 --- a/src/components/ui/forms/ABISelector.tsx +++ b/src/components/ui/forms/ABISelector.tsx @@ -4,8 +4,9 @@ import detectProxyTarget from 'evm-proxy-detection'; import { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { isAddress } from 'viem'; -import { useEnsAddress, usePublicClient } from 'wagmi'; +import { useEnsAddress } from 'wagmi'; import { logError } from '../../../helpers/errorLogging'; +import useNetworkPublicClient from '../../../hooks/useNetworkPublicClient'; import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { LabelComponent } from './InputComponent'; @@ -29,11 +30,11 @@ export default function ABISelector({ target, onChange }: IABISelector) { const { etherscanAPIUrl } = useNetworkConfigStore(); const { t } = useTranslation('common'); const { data: ensAddress } = useEnsAddress({ name: target?.toLowerCase() }); - const client = usePublicClient(); + const client = useNetworkPublicClient(); useEffect(() => { const loadABI = async () => { - if (client && target && ((ensAddress && isAddress(ensAddress)) || isAddress(target))) { + if (target && ((ensAddress && isAddress(ensAddress)) || isAddress(target))) { try { const requestFunc = ({ method, params }: { method: any; params: any }) => client.request({ method, params }); diff --git a/src/components/ui/modals/DelegateModal.tsx b/src/components/ui/modals/DelegateModal.tsx index d26f570f0..c668502de 100644 --- a/src/components/ui/modals/DelegateModal.tsx +++ b/src/components/ui/modals/DelegateModal.tsx @@ -3,10 +3,11 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { Field, FieldAttributes, Formik } from 'formik'; import { useTranslation } from 'react-i18next'; import { getAddress, getContract, zeroAddress } from 'viem'; -import { useAccount, usePublicClient, useWalletClient } from 'wagmi'; +import { useAccount, useWalletClient } from 'wagmi'; import * as Yup from 'yup'; import LockReleaseAbi from '../../../assets/abi/LockRelease'; import { useValidationAddress } from '../../../hooks/schemas/common/useValidationAddress'; +import useNetworkPublicClient from '../../../hooks/useNetworkPublicClient'; import { useGetAccountName } from '../../../hooks/utils/useGetAccountName'; import { useTransaction } from '../../../hooks/utils/useTransaction'; import { useFractal } from '../../../providers/App/AppProvider'; @@ -36,12 +37,12 @@ export function DelegateModal({ close }: { close: Function }) { const [contractCall, pending] = useTransaction(); const { addressValidationTest } = useValidationAddress(); const { data: walletClient } = useWalletClient(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const submitDelegation = async (values: { address: string }) => { if (!votesTokenAddress || !walletClient) return; let validAddress = values.address; - if (validateENSName(validAddress) && publicClient) { + if (validateENSName(validAddress)) { const maybeEnsAddress = await publicClient.getEnsAddress({ name: values.address }); if (maybeEnsAddress) { validAddress = maybeEnsAddress; @@ -65,7 +66,7 @@ export function DelegateModal({ close }: { close: Function }) { }); }; const submitLockedDelegation = async (values: { address: string }) => { - if (!lockReleaseAddress || !publicClient || !walletClient) return; + if (!lockReleaseAddress || !walletClient) return; let validAddress = values.address; if (validateENSName(validAddress)) { const maybeEnsAddress = await publicClient.getEnsAddress({ name: values.address }); diff --git a/src/components/ui/modals/ForkProposalTemplateModal.tsx b/src/components/ui/modals/ForkProposalTemplateModal.tsx index 4f344f614..b8f84fc69 100644 --- a/src/components/ui/modals/ForkProposalTemplateModal.tsx +++ b/src/components/ui/modals/ForkProposalTemplateModal.tsx @@ -3,10 +3,10 @@ import { ChangeEventHandler, useState, useCallback, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { getAddress } from 'viem'; -import { usePublicClient } from 'wagmi'; import { DAO_ROUTES } from '../../../constants/routes'; import { useIsSafe } from '../../../hooks/safe/useIsSafe'; import { validateAddress } from '../../../hooks/schemas/common/useValidationAddress'; +import useNetworkPublicClient from '../../../hooks/useNetworkPublicClient'; import { useCanUserCreateProposal } from '../../../hooks/utils/useCanUserSubmitProposal'; import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; @@ -31,7 +31,7 @@ export default function ForkProposalTemplateModal({ const { t } = useTranslation('proposalTemplate'); const navigate = useNavigate(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const { chain, addressPrefix } = useNetworkConfigStore(); const { subgraphInfo } = useDaoInfoStore(); @@ -44,7 +44,7 @@ export default function ForkProposalTemplateModal({ }; const validateDAOAddress = useCallback(async () => { - if (!inputValue || isSafeLoading || !publicClient) { + if (!inputValue || isSafeLoading) { setError(''); return false; } diff --git a/src/components/ui/modals/SendAssetsModal.tsx b/src/components/ui/modals/SendAssetsModal.tsx index 91a7d0fd7..ed959ab64 100644 --- a/src/components/ui/modals/SendAssetsModal.tsx +++ b/src/components/ui/modals/SendAssetsModal.tsx @@ -4,9 +4,9 @@ import { Field, FieldAttributes, FieldProps, Form, Formik } from 'formik'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Address, getAddress } from 'viem'; -import { usePublicClient } from 'wagmi'; import * as Yup from 'yup'; import { useValidationAddress } from '../../../hooks/schemas/common/useValidationAddress'; +import useNetworkPublicClient from '../../../hooks/useNetworkPublicClient'; import { useFractal } from '../../../providers/App/AppProvider'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { BigIntValuePair, TokenBalance } from '../../../types'; @@ -47,7 +47,7 @@ export function SendAssetsModal({ } = useFractal(); const { safe } = useDaoInfoStore(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const { t } = useTranslation(['modals', 'common']); const fungibleAssetsWithBalance = assetsFungible.filter(asset => parseFloat(asset.balance) > 0); @@ -75,7 +75,7 @@ export function SendAssetsModal({ const handleSendAssetsSubmit = async (values: SendAssetsFormValues) => { let destAddress = values.destinationAddress; - if (validateENSName(values.destinationAddress) && publicClient) { + if (validateENSName(values.destinationAddress)) { const ensAddress = await publicClient.getEnsAddress({ name: values.destinationAddress }); if (ensAddress === null) { throw new Error('Invalid ENS name'); diff --git a/src/components/ui/proposal/useProposalCountdown.tsx b/src/components/ui/proposal/useProposalCountdown.tsx index 557153c9a..852bdfb50 100644 --- a/src/components/ui/proposal/useProposalCountdown.tsx +++ b/src/components/ui/proposal/useProposalCountdown.tsx @@ -1,11 +1,11 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback, useEffect, useRef, useState } from 'react'; import { getContract } from 'viem'; -import { usePublicClient } from 'wagmi'; import { logError } from '../../../helpers/errorLogging'; import useSnapshotProposal from '../../../hooks/DAO/loaders/snapshot/useSnapshotProposal'; import { useLoadDAOProposals } from '../../../hooks/DAO/loaders/useLoadDAOProposals'; import useUpdateProposalState from '../../../hooks/DAO/proposal/useUpdateProposalState'; +import useNetworkPublicClient from '../../../hooks/useNetworkPublicClient'; import { useFractal } from '../../../providers/App/AppProvider'; import { AzoriusGovernance, @@ -24,7 +24,7 @@ export function useProposalCountdown(proposal: FractalProposal) { governanceContracts, action, } = useFractal(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const [secondsLeft, setSecondsLeft] = useState(); const { snapshotProposal } = useSnapshotProposal(proposal); @@ -90,8 +90,6 @@ export function useProposalCountdown(proposal: FractalProposal) { }, []); const getCountdown = useCallback(async () => { - if (!publicClient) return; - const freezeGuard = freezeGuardContractAddress !== undefined && freezeGuardType === FreezeGuardType.MULTISIG ? getContract({ diff --git a/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts b/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts index 45231443f..715e65f53 100644 --- a/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts +++ b/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts @@ -1,7 +1,6 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useEffect, useMemo } from 'react'; import { getContract } from 'viem'; -import { usePublicClient } from 'wagmi'; import { logError } from '../../../../helpers/errorLogging'; import { useFractal } from '../../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; @@ -12,6 +11,7 @@ import { mapProposalCreatedEventToProposal, } from '../../../../utils'; import { getAverageBlockTime } from '../../../../utils/contract'; +import useNetworkPublicClient from '../../../useNetworkPublicClient'; import { useAddressContractType } from '../../../utils/useAddressContractType'; import { useSafeDecoder } from '../../../utils/useSafeDecoder'; @@ -27,11 +27,11 @@ export const useAzoriusListeners = () => { }, } = useFractal(); const decode = useSafeDecoder(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const { getAddressContractType } = useAddressContractType(); const azoriusContract = useMemo(() => { - if (!publicClient || !moduleAzoriusAddress) { + if (!moduleAzoriusAddress) { return; } @@ -43,7 +43,7 @@ export const useAzoriusListeners = () => { }, [moduleAzoriusAddress, publicClient]); const erc20StrategyContract = useMemo(() => { - if (!linearVotingErc20Address || !publicClient) { + if (!linearVotingErc20Address) { return undefined; } @@ -55,7 +55,7 @@ export const useAzoriusListeners = () => { }, [linearVotingErc20Address, publicClient]); const erc20WithHatsProposalCreationStrategyContract = useMemo(() => { - if (!linearVotingErc20WithHatsWhitelistingAddress || !publicClient) { + if (!linearVotingErc20WithHatsWhitelistingAddress) { return undefined; } @@ -67,7 +67,7 @@ export const useAzoriusListeners = () => { }, [linearVotingErc20WithHatsWhitelistingAddress, publicClient]); const erc721StrategyContract = useMemo(() => { - if (!linearVotingErc721Address || !publicClient) { + if (!linearVotingErc721Address) { return undefined; } @@ -79,7 +79,7 @@ export const useAzoriusListeners = () => { }, [linearVotingErc721Address, publicClient]); const erc721WithHatsProposalCreationStrategyContract = useMemo(() => { - if (!linearVotingErc721WithHatsWhitelistingAddress || !publicClient) { + if (!linearVotingErc721WithHatsWhitelistingAddress) { return undefined; } @@ -103,8 +103,7 @@ export const useAzoriusListeners = () => { !log.args.proposalId || !log.args.metadata || !log.args.transactions || - !log.args.proposer || - !publicClient + !log.args.proposer ) { continue; } diff --git a/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts b/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts index fc00c8565..a2f3ca841 100644 --- a/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts +++ b/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts @@ -8,7 +8,6 @@ import { PublicClient, getContract, } from 'viem'; -import { usePublicClient } from 'wagmi'; import { logError } from '../../../../helpers/errorLogging'; import { useFractal } from '../../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; @@ -20,6 +19,7 @@ import { } from '../../../../types'; import { AzoriusProposal } from '../../../../types/daoProposal'; import { decodeTransactions, mapProposalCreatedEventToProposal } from '../../../../utils'; +import useNetworkPublicClient from '../../../useNetworkPublicClient'; import { CacheExpiry, CacheKeys } from '../../../utils/cache/cacheDefaults'; import { getValue, setValue } from '../../../utils/cache/useLocalStorage'; import { useAddressContractType } from '../../../utils/useAddressContractType'; @@ -41,11 +41,11 @@ export const useAzoriusProposals = () => { action, } = useFractal(); const decode = useSafeDecoder(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const { getAddressContractType } = useAddressContractType(); const azoriusContract = useMemo(() => { - if (!moduleAzoriusAddress || !publicClient) { + if (!moduleAzoriusAddress) { return; } @@ -58,10 +58,7 @@ export const useAzoriusProposals = () => { const erc20VotedEvents = useMemo(async () => { let events: GetContractEventsReturnType | undefined; - if ( - (!linearVotingErc20Address && !linearVotingErc20WithHatsWhitelistingAddress) || - !publicClient - ) { + if (!linearVotingErc20Address && !linearVotingErc20WithHatsWhitelistingAddress) { return; } @@ -90,10 +87,7 @@ export const useAzoriusProposals = () => { const erc721VotedEvents = useMemo(async () => { let events: GetContractEventsReturnType | undefined; - if ( - (!linearVotingErc721Address && !linearVotingErc721WithHatsWhitelistingAddress) || - !publicClient - ) { + if (!linearVotingErc721Address && !linearVotingErc721WithHatsWhitelistingAddress) { return; } diff --git a/src/hooks/DAO/loaders/governance/useERC20Claim.ts b/src/hooks/DAO/loaders/governance/useERC20Claim.ts index 80bd41e91..40a522ad0 100644 --- a/src/hooks/DAO/loaders/governance/useERC20Claim.ts +++ b/src/hooks/DAO/loaders/governance/useERC20Claim.ts @@ -1,10 +1,10 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback, useEffect, useRef } from 'react'; import { getContract } from 'viem'; -import { usePublicClient } from 'wagmi'; import { useFractal } from '../../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; import { useDaoInfoStore } from '../../../../store/daoInfo/useDaoInfoStore'; +import useNetworkPublicClient from '../../../useNetworkPublicClient'; import { useAddressContractType } from '../../../utils/useAddressContractType'; export function useERC20Claim() { @@ -13,12 +13,12 @@ export function useERC20Claim() { action, } = useFractal(); const { safe } = useDaoInfoStore(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const safeAddress = safe?.address; const { getAddressContractType } = useAddressContractType(); const loadTokenClaimContract = useCallback(async () => { - if (!votesTokenAddress || !publicClient) { + if (!votesTokenAddress) { return; } diff --git a/src/hooks/DAO/loaders/governance/useERC20LinearStrategy.ts b/src/hooks/DAO/loaders/governance/useERC20LinearStrategy.ts index b07102607..b6f86263d 100644 --- a/src/hooks/DAO/loaders/governance/useERC20LinearStrategy.ts +++ b/src/hooks/DAO/loaders/governance/useERC20LinearStrategy.ts @@ -1,11 +1,11 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback, useMemo } from 'react'; import { formatUnits, getContract } from 'viem'; -import { usePublicClient } from 'wagmi'; import { useFractal } from '../../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; import { AzoriusGovernance, VotingStrategyType } from '../../../../types'; import { blocksToSeconds } from '../../../../utils/contract'; +import useNetworkPublicClient from '../../../useNetworkPublicClient'; import { useTimeHelpers } from '../../../utils/useTimeHelpers'; export const useERC20LinearStrategy = () => { @@ -19,12 +19,12 @@ export const useERC20LinearStrategy = () => { action, } = useFractal(); const { getTimeDuration } = useTimeHelpers(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const ozLinearVotingContract = useMemo(() => { const votingStrategyAddress = linearVotingErc20Address || linearVotingErc20WithHatsWhitelistingAddress; - if (!votingStrategyAddress || !publicClient) { + if (!votingStrategyAddress) { return; } @@ -36,7 +36,7 @@ export const useERC20LinearStrategy = () => { }, [linearVotingErc20Address, linearVotingErc20WithHatsWhitelistingAddress, publicClient]); const loadERC20Strategy = useCallback(async () => { - if (!ozLinearVotingContract || !moduleAzoriusAddress || !publicClient) { + if (!ozLinearVotingContract || !moduleAzoriusAddress) { return {}; } diff --git a/src/hooks/DAO/loaders/governance/useERC20LinearToken.ts b/src/hooks/DAO/loaders/governance/useERC20LinearToken.ts index 3ed34423d..3ce366749 100644 --- a/src/hooks/DAO/loaders/governance/useERC20LinearToken.ts +++ b/src/hooks/DAO/loaders/governance/useERC20LinearToken.ts @@ -1,9 +1,10 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback, useEffect, useRef } from 'react'; import { getContract } from 'viem'; -import { useAccount, usePublicClient } from 'wagmi'; +import { useAccount } from 'wagmi'; import { useFractal } from '../../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; +import useNetworkPublicClient from '../../../useNetworkPublicClient'; export const useERC20LinearToken = ({ onMount = true }: { onMount?: boolean }) => { const isTokenLoaded = useRef(false); @@ -15,10 +16,10 @@ export const useERC20LinearToken = ({ onMount = true }: { onMount?: boolean }) = } = useFractal(); const user = useAccount(); const account = user.address; - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const loadERC20Token = useCallback(async () => { - if (!votesTokenAddress || !publicClient) { + if (!votesTokenAddress) { return; } @@ -46,7 +47,7 @@ export const useERC20LinearToken = ({ onMount = true }: { onMount?: boolean }) = }, [action, publicClient, votesTokenAddress]); const loadERC20TokenAccountData = useCallback(async () => { - if (!account || !votesTokenAddress || !publicClient) { + if (!account || !votesTokenAddress) { action.dispatch({ type: FractalGovernanceAction.RESET_TOKEN_ACCOUNT_DATA }); return; } @@ -92,7 +93,7 @@ export const useERC20LinearToken = ({ onMount = true }: { onMount?: boolean }) = return; } - if (!votesTokenAddress || !publicClient) { + if (!votesTokenAddress) { return; } @@ -117,7 +118,7 @@ export const useERC20LinearToken = ({ onMount = true }: { onMount?: boolean }) = return; } - if (!votesTokenAddress || !publicClient) { + if (!votesTokenAddress) { return; } @@ -152,7 +153,7 @@ export const useERC20LinearToken = ({ onMount = true }: { onMount?: boolean }) = return; } - if (!votesTokenAddress || !publicClient) { + if (!votesTokenAddress) { return; } diff --git a/src/hooks/DAO/loaders/governance/useERC721LinearStrategy.ts b/src/hooks/DAO/loaders/governance/useERC721LinearStrategy.ts index d4cb8d9e0..7e7609f64 100644 --- a/src/hooks/DAO/loaders/governance/useERC721LinearStrategy.ts +++ b/src/hooks/DAO/loaders/governance/useERC721LinearStrategy.ts @@ -1,11 +1,11 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback, useMemo } from 'react'; import { getContract } from 'viem'; -import { usePublicClient } from 'wagmi'; import { useFractal } from '../../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; import { VotingStrategyType } from '../../../../types'; import { blocksToSeconds } from '../../../../utils/contract'; +import useNetworkPublicClient from '../../../useNetworkPublicClient'; import { useTimeHelpers } from '../../../utils/useTimeHelpers'; export const useERC721LinearStrategy = () => { @@ -18,12 +18,12 @@ export const useERC721LinearStrategy = () => { action, } = useFractal(); const { getTimeDuration } = useTimeHelpers(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const erc721LinearVotingContract = useMemo(() => { const votingStrategyAddress = linearVotingErc721Address || linearVotingErc721WithHatsWhitelistingAddress; - if (!votingStrategyAddress || !publicClient) { + if (!votingStrategyAddress) { return; } @@ -35,7 +35,7 @@ export const useERC721LinearStrategy = () => { }, [linearVotingErc721Address, linearVotingErc721WithHatsWhitelistingAddress, publicClient]); const loadERC721Strategy = useCallback(async () => { - if (!moduleAzoriusAddress || !erc721LinearVotingContract || !publicClient) { + if (!moduleAzoriusAddress || !erc721LinearVotingContract) { return; } diff --git a/src/hooks/DAO/loaders/governance/useERC721Tokens.ts b/src/hooks/DAO/loaders/governance/useERC721Tokens.ts index e90e59c6e..473dd01f6 100644 --- a/src/hooks/DAO/loaders/governance/useERC721Tokens.ts +++ b/src/hooks/DAO/loaders/governance/useERC721Tokens.ts @@ -1,19 +1,19 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback } from 'react'; import { erc721Abi, getContract, zeroAddress } from 'viem'; -import { usePublicClient } from 'wagmi'; import { useFractal } from '../../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; import { ERC721TokenData } from '../../../../types'; +import useNetworkPublicClient from '../../../useNetworkPublicClient'; export default function useERC721Tokens() { const { governanceContracts: { linearVotingErc721Address }, action, } = useFractal(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const loadERC721Tokens = useCallback(async () => { - if (!linearVotingErc721Address || !publicClient) { + if (!linearVotingErc721Address) { return; } const erc721LinearVotingContract = getContract({ diff --git a/src/hooks/DAO/loaders/governance/useLockRelease.ts b/src/hooks/DAO/loaders/governance/useLockRelease.ts index e55fe59b7..a1d7dd849 100644 --- a/src/hooks/DAO/loaders/governance/useLockRelease.ts +++ b/src/hooks/DAO/loaders/governance/useLockRelease.ts @@ -1,9 +1,10 @@ import { useCallback, useEffect, useMemo, useRef } from 'react'; import { getContract } from 'viem'; -import { useAccount, usePublicClient } from 'wagmi'; +import { useAccount } from 'wagmi'; import LockReleaseAbi from '../../../../assets/abi/LockRelease'; import { useFractal } from '../../../../providers/App/AppProvider'; import { DecentGovernanceAction } from '../../../../providers/App/governance/action'; +import useNetworkPublicClient from '../../../useNetworkPublicClient'; /** * @link https://github.com/decentdao/dcnt/blob/master/contracts/LockRelease.sol @@ -18,10 +19,10 @@ export const useLockRelease = ({ onMount = true }: { onMount?: boolean }) => { action, } = useFractal(); const user = useAccount(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const lockReleaseContract = useMemo(() => { - if (!lockReleaseAddress || !publicClient) { + if (!lockReleaseAddress) { return; } @@ -33,7 +34,7 @@ export const useLockRelease = ({ onMount = true }: { onMount?: boolean }) => { }, [lockReleaseAddress, publicClient]); const loadLockedVotesToken = useCallback(async () => { - if (!lockReleaseContract || !user.address || !publicClient) { + if (!lockReleaseContract || !user.address) { action.dispatch({ type: DecentGovernanceAction.RESET_LOCKED_TOKEN_ACCOUNT_DATA }); return; } @@ -56,7 +57,7 @@ export const useLockRelease = ({ onMount = true }: { onMount?: boolean }) => { type: DecentGovernanceAction.SET_LOCKED_TOKEN_ACCOUNT_DATA, payload: tokenAccountData, }); - }, [action, lockReleaseContract, publicClient, user.address]); + }, [action, lockReleaseContract, user.address]); useEffect(() => { if (!user.address) { @@ -75,7 +76,7 @@ export const useLockRelease = ({ onMount = true }: { onMount?: boolean }) => { }, [loadLockedVotesToken, lockReleaseAddress, onMount, user.address]); useEffect(() => { - if (!lockReleaseContract || !onMount || !publicClient || !user.address) { + if (!lockReleaseContract || !onMount || !user.address) { return; } @@ -87,10 +88,10 @@ export const useLockRelease = ({ onMount = true }: { onMount?: boolean }) => { return () => { unwatch(); }; - }, [loadLockedVotesToken, lockReleaseContract, onMount, publicClient, user.address]); + }, [loadLockedVotesToken, lockReleaseContract, onMount, user.address]); useEffect(() => { - if (!lockReleaseContract || !onMount || !publicClient || !user.address) { + if (!lockReleaseContract || !onMount || !user.address) { return; } @@ -113,7 +114,7 @@ export const useLockRelease = ({ onMount = true }: { onMount?: boolean }) => { unwatchToDelegate(); unwatchFromDelegate(); }; - }, [loadLockedVotesToken, lockReleaseContract, onMount, publicClient, user.address]); + }, [loadLockedVotesToken, lockReleaseContract, onMount, user.address]); return { loadLockedVotesToken }; }; diff --git a/src/hooks/DAO/loaders/useDecentModules.ts b/src/hooks/DAO/loaders/useDecentModules.ts index da1311891..b34c1fd19 100644 --- a/src/hooks/DAO/loaders/useDecentModules.ts +++ b/src/hooks/DAO/loaders/useDecentModules.ts @@ -1,12 +1,10 @@ import { useCallback } from 'react'; import { getAddress } from 'viem'; -import { usePublicClient } from 'wagmi'; import { DecentModule, FractalModuleType } from '../../../types'; import { useAddressContractType } from '../../utils/useAddressContractType'; export const useDecentModules = () => { const { getAddressContractType } = useAddressContractType(); - const publicClient = usePublicClient(); const lookupModules = useCallback( async (_moduleAddresses: string[]) => { const modules = await Promise.all( @@ -17,12 +15,12 @@ export const useDecentModules = () => { let safeModule: DecentModule; - if (masterCopyData.isModuleAzorius && publicClient) { + if (masterCopyData.isModuleAzorius) { safeModule = { moduleAddress: moduleAddress, moduleType: FractalModuleType.AZORIUS, }; - } else if (masterCopyData.isModuleFractal && publicClient) { + } else if (masterCopyData.isModuleFractal) { safeModule = { moduleAddress: moduleAddress, moduleType: FractalModuleType.FRACTAL, @@ -39,7 +37,7 @@ export const useDecentModules = () => { ); return modules; }, - [getAddressContractType, publicClient], + [getAddressContractType], ); return lookupModules; }; diff --git a/src/hooks/DAO/loaders/useDecentTreasury.ts b/src/hooks/DAO/loaders/useDecentTreasury.ts index 8c5447a21..5f845d0a3 100644 --- a/src/hooks/DAO/loaders/useDecentTreasury.ts +++ b/src/hooks/DAO/loaders/useDecentTreasury.ts @@ -7,7 +7,6 @@ import { import { useEffect, useCallback, useRef } from 'react'; import { toast } from 'sonner'; import { Address, erc20Abi, getAddress, zeroAddress } from 'viem'; -import { usePublicClient } from 'wagmi'; import { useFractal } from '../../../providers/App/AppProvider'; import useBalancesAPI from '../../../providers/App/hooks/useBalancesAPI'; import { useSafeAPI } from '../../../providers/App/hooks/useSafeAPI'; @@ -22,6 +21,7 @@ import { } from '../../../types'; import { formatCoin } from '../../../utils'; import { MOCK_MORALIS_ETH_ADDRESS } from '../../../utils/address'; +import useNetworkPublicClient from '../../useNetworkPublicClient'; import { CacheExpiry, CacheKeys } from '../../utils/cache/cacheDefaults'; import { setValue } from '../../utils/cache/useLocalStorage'; @@ -46,7 +46,7 @@ export const useDecentTreasury = () => { const { chain, nativeTokenIcon } = useNetworkConfigStore(); const safeAddress = safe?.address; - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const formatTransfer = useCallback( ({ transfer, isLast }: { transfer: TransferWithTokenInfo; isLast: boolean }) => { @@ -195,9 +195,6 @@ export const useDecentTreasury = () => { if (!fallbackTokenData) { // Fallback to blockchain call if token info not available - if (!publicClient) { - throw new Error('No public client'); - } const [name, symbol, decimals] = await Promise.all([ publicClient.readContract({ address, abi: erc20Abi, functionName: 'name' }), publicClient.readContract({ address, abi: erc20Abi, functionName: 'symbol' }), diff --git a/src/hooks/DAO/loaders/useFractalFreeze.ts b/src/hooks/DAO/loaders/useFractalFreeze.ts index 37122106f..bcde30579 100644 --- a/src/hooks/DAO/loaders/useFractalFreeze.ts +++ b/src/hooks/DAO/loaders/useFractalFreeze.ts @@ -1,7 +1,7 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback, useEffect, useRef } from 'react'; import { Address, GetContractReturnType, PublicClient, getContract, zeroAddress } from 'viem'; -import { useAccount, usePublicClient } from 'wagmi'; +import { useAccount } from 'wagmi'; import GnosisSafeL2Abi from '../../../assets/abi/GnosisSafeL2'; import { isWithinFreezeProposalPeriod, @@ -11,6 +11,7 @@ import { useFractal } from '../../../providers/App/AppProvider'; import { FractalGuardAction } from '../../../providers/App/guard/action'; import { FractalGuardContracts, FreezeVotingType } from '../../../types'; import { blocksToSeconds, getTimeStamp } from '../../../utils/contract'; +import useNetworkPublicClient from '../../useNetworkPublicClient'; import { useAddressContractType } from '../../utils/useAddressContractType'; import useUserERC721VotingTokens from '../proposal/useUserERC721VotingTokens'; import { FreezeGuard } from './../../../types/fractal'; @@ -34,7 +35,7 @@ export const useFractalFreeze = ({ loadOnMount, ); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const { getAddressContractType } = useAddressContractType(); const loadFractalFreezeGuard = useCallback( @@ -42,7 +43,7 @@ export const useFractalFreeze = ({ freezeVotingContractAddress, freezeVotingType: freezeVotingType, }: FractalGuardContracts) => { - if (freezeVotingType == null || !freezeVotingContractAddress || !account || !publicClient) { + if (freezeVotingType == null || !freezeVotingContractAddress || !account) { return; } @@ -220,7 +221,6 @@ export const useFractalFreeze = ({ if ( !loadOnMount || !freezeVotingContractAddress || - !publicClient || !isFreezeSet.current || freezeVotingType !== FreezeVotingType.MULTISIG ) { @@ -270,7 +270,6 @@ export const useFractalFreeze = ({ if ( !loadOnMount || !freezeVotingContractAddress || - !publicClient || !isFreezeSet.current || freezeVotingType !== FreezeVotingType.ERC721 ) { @@ -320,7 +319,6 @@ export const useFractalFreeze = ({ if ( !loadOnMount || !freezeVotingContractAddress || - !publicClient || !isFreezeSet.current || freezeVotingType !== FreezeVotingType.ERC20 ) { diff --git a/src/hooks/DAO/loaders/useFractalGuardContracts.ts b/src/hooks/DAO/loaders/useFractalGuardContracts.ts index 61e14095c..0e7e95443 100644 --- a/src/hooks/DAO/loaders/useFractalGuardContracts.ts +++ b/src/hooks/DAO/loaders/useFractalGuardContracts.ts @@ -1,12 +1,12 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback, useEffect, useRef } from 'react'; import { getContract, zeroAddress } from 'viem'; -import { usePublicClient } from 'wagmi'; import { useFractal } from '../../../providers/App/AppProvider'; import { GuardContractAction } from '../../../providers/App/guardContracts/action'; import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { FreezeGuardType, FreezeVotingType } from '../../../types'; +import useNetworkPublicClient from '../../useNetworkPublicClient'; import { useAddressContractType } from '../../utils/useAddressContractType'; import { DecentModule, FractalModuleType, GnosisSafe } from './../../../types/fractal'; @@ -23,14 +23,10 @@ export const useFractalGuardContracts = ({ loadOnMount = true }: { loadOnMount?: const { getAddressContractType } = useAddressContractType(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const loadFractalGuardContracts = useCallback( async (_safe: GnosisSafe, _fractalModules: DecentModule[]) => { - if (!publicClient) { - return; - } - const { guard } = _safe; const azoriusModule = _fractalModules?.find( diff --git a/src/hooks/DAO/loaders/useGovernanceContracts.ts b/src/hooks/DAO/loaders/useGovernanceContracts.ts index 56e7a7c60..ee8deeec9 100644 --- a/src/hooks/DAO/loaders/useGovernanceContracts.ts +++ b/src/hooks/DAO/loaders/useGovernanceContracts.ts @@ -1,13 +1,13 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback, useEffect, useRef } from 'react'; import { Address, getContract } from 'viem'; -import { usePublicClient } from 'wagmi'; 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 useNetworkPublicClient from '../../useNetworkPublicClient'; import { useAddressContractType } from '../../utils/useAddressContractType'; import useVotingStrategyAddress from '../../utils/useVotingStrategiesAddresses'; @@ -16,7 +16,7 @@ export const useGovernanceContracts = () => { const currentValidAddress = useRef(); const { action } = useFractal(); const node = useDaoInfoStore(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const { getAddressContractType } = useAddressContractType(); const { getVotingStrategies } = useVotingStrategyAddress(); @@ -37,10 +37,6 @@ export const useGovernanceContracts = () => { return; } - if (!publicClient) { - throw new Error('Public Client is not set!'); - } - let linearVotingErc20Address: Address | undefined; let linearVotingErc721Address: Address | undefined; let linearVotingErc20WithHatsWhitelistingAddress: Address | undefined; diff --git a/src/hooks/DAO/loaders/useHatsTree.ts b/src/hooks/DAO/loaders/useHatsTree.ts index 92540b53a..804ccfc9b 100644 --- a/src/hooks/DAO/loaders/useHatsTree.ts +++ b/src/hooks/DAO/loaders/useHatsTree.ts @@ -4,13 +4,13 @@ import { useCallback, useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { toast } from 'sonner'; import { PublicClient } from 'viem'; -import { usePublicClient } from 'wagmi'; import { useFractal } from '../../../providers/App/AppProvider'; import useIPFSClient from '../../../providers/App/hooks/useIPFSClient'; import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { DecentHatsError } from '../../../store/roles/rolesStoreUtils'; import { useRolesStore } from '../../../store/roles/useRolesStore'; +import useNetworkPublicClient from '../../useNetworkPublicClient'; import { CacheExpiry, CacheKeys } from '../../utils/cache/cacheDefaults'; import { getValue, setValue } from '../../utils/cache/useLocalStorage'; @@ -36,7 +36,7 @@ const useHatsTree = () => { hatsElectionsEligibilityMasterCopy: hatsElectionsImplementation, }, } = useNetworkConfigStore(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const apolloClient = useApolloClient(); const getHatsTree = useCallback( @@ -163,7 +163,6 @@ const useHatsTree = () => { if ( !!hatsTreeId && !!contextChainId && - !!publicClient && key !== null && key !== daoHatTreeloadKey.current && previousHatsTreeId !== `${hatsTreeId}` // don't try to load hats tree if this new DAO is stuck with the same hats tree id as the previous DAO diff --git a/src/hooks/DAO/proposal/useCreateProposalTemplate.ts b/src/hooks/DAO/proposal/useCreateProposalTemplate.ts index c5cc8063f..89e4477a3 100644 --- a/src/hooks/DAO/proposal/useCreateProposalTemplate.ts +++ b/src/hooks/DAO/proposal/useCreateProposalTemplate.ts @@ -3,13 +3,13 @@ import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { encodeFunctionData } from 'viem'; import { normalize } from 'viem/ens'; -import { usePublicClient } from 'wagmi'; import { useFractal } from '../../../providers/App/AppProvider'; import useIPFSClient from '../../../providers/App/hooks/useIPFSClient'; import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore'; import { ProposalExecuteData } from '../../../types'; import { CreateProposalForm } from '../../../types/proposalBuilder'; import { validateENSName } from '../../../utils/url'; +import useNetworkPublicClient from '../../useNetworkPublicClient'; const customSerializer = (_: string, value: any) => { if (typeof value === 'bigint') { @@ -20,7 +20,7 @@ const customSerializer = (_: string, value: any) => { }; export default function useCreateProposalTemplate() { - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const client = useIPFSClient(); const { governance: { proposalTemplates }, @@ -34,7 +34,7 @@ export default function useCreateProposalTemplate() { const prepareProposalTemplateProposal = useCallback( async (values: CreateProposalForm) => { - if (proposalTemplates && publicClient) { + if (proposalTemplates) { const proposalMetadata = { title: t('createProposalTemplateTitle'), description: t('createProposalTemplateDescription'), diff --git a/src/hooks/DAO/proposal/usePrepareProposal.ts b/src/hooks/DAO/proposal/usePrepareProposal.ts index 44dbaaa0c..fbb76dd22 100644 --- a/src/hooks/DAO/proposal/usePrepareProposal.ts +++ b/src/hooks/DAO/proposal/usePrepareProposal.ts @@ -1,12 +1,12 @@ import { useCallback } from 'react'; import { Hex, getAddress } from 'viem'; -import { usePublicClient } from 'wagmi'; import { CreateProposalForm } from '../../../types/proposalBuilder'; import { encodeFunction } from '../../../utils/crypto'; import { validateENSName, isValidUrl } from '../../../utils/url'; +import useNetworkPublicClient from '../../useNetworkPublicClient'; export function usePrepareProposal() { - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const prepareProposal = useCallback( async (values: CreateProposalForm) => { @@ -35,7 +35,7 @@ export function usePrepareProposal() { }); const targets = await Promise.all( transactionsWithEncoding.map(async tx => { - if (tx.targetAddress && validateENSName(tx.targetAddress) && publicClient) { + if (tx.targetAddress && validateENSName(tx.targetAddress)) { const address = await publicClient.getEnsAddress({ name: tx.targetAddress }); if (address) { return address; diff --git a/src/hooks/DAO/proposal/useSubmitProposal.ts b/src/hooks/DAO/proposal/useSubmitProposal.ts index 64a33cb4f..1c16948bd 100644 --- a/src/hooks/DAO/proposal/useSubmitProposal.ts +++ b/src/hooks/DAO/proposal/useSubmitProposal.ts @@ -12,7 +12,7 @@ import { isHex, parseAbiParameters, } from 'viem'; -import { useAccount, usePublicClient, useWalletClient } from 'wagmi'; +import { useAccount, useWalletClient } from 'wagmi'; import MultiSendCallOnlyAbi from '../../../assets/abi/MultiSendCallOnly'; import { ADDRESS_MULTISIG_METADATA } from '../../../constants/common'; import { buildSafeAPIPost, encodeMultiSend } from '../../../helpers'; @@ -25,6 +25,7 @@ import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetwo import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { CreateProposalMetadata, MetaTransaction, ProposalExecuteData } from '../../../types'; import { buildSafeApiUrl, getAzoriusModuleFromModules } from '../../../utils'; +import useNetworkPublicClient from '../../useNetworkPublicClient'; import useVotingStrategiesAddresses from '../../utils/useVotingStrategiesAddresses'; import { useDecentModules } from '../loaders/useDecentModules'; import { useLoadDAOProposals } from '../loaders/useLoadDAOProposals'; @@ -62,7 +63,7 @@ export default function useSubmitProposal() { const [pendingCreateTx, setPendingCreateTx] = useState(false); const loadDAOProposals = useLoadDAOProposals(); const { data: walletClient } = useWalletClient(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const { getVotingStrategies } = useVotingStrategiesAddresses(); const { address: userAddress } = useAccount(); @@ -246,7 +247,7 @@ export default function useSubmitProposal() { failedToastMessage, safeAddress, }: ISubmitAzoriusProposal) => { - if (!proposalData || !walletClient || !publicClient) { + if (!proposalData || !walletClient) { return; } const toastId = toast.loading(pendingToastMessage, { @@ -308,7 +309,7 @@ export default function useSubmitProposal() { successCallback, safeAddress, }: ISubmitProposal) => { - if (!proposalData || !safeAPI || !publicClient || !userAddress) { + if (!proposalData || !safeAPI || !userAddress) { return; } diff --git a/src/hooks/DAO/proposal/useUpdateProposalState.ts b/src/hooks/DAO/proposal/useUpdateProposalState.ts index 5ce4cfd51..544e1ca11 100644 --- a/src/hooks/DAO/proposal/useUpdateProposalState.ts +++ b/src/hooks/DAO/proposal/useUpdateProposalState.ts @@ -1,13 +1,13 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback, Dispatch } from 'react'; import { getContract } from 'viem'; -import { usePublicClient } from 'wagmi'; import { FractalGovernanceAction, FractalGovernanceActions, } from '../../../providers/App/governance/action'; import { FractalGovernanceContracts } from '../../../types'; import { getAzoriusProposalState } from '../../../utils'; +import useNetworkPublicClient from '../../useNetworkPublicClient'; interface IUseUpdateProposalState { governanceContracts: FractalGovernanceContracts; @@ -18,10 +18,10 @@ export default function useUpdateProposalState({ governanceContracts: { moduleAzoriusAddress }, governanceDispatch, }: IUseUpdateProposalState) { - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const updateProposalState = useCallback( async (proposalId: number) => { - if (!moduleAzoriusAddress || !publicClient) { + if (!moduleAzoriusAddress) { return; } const azoriusContract = getContract({ diff --git a/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts b/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts index a6bdfd4e2..eef107e2d 100644 --- a/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts +++ b/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts @@ -1,11 +1,12 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback, useEffect, useState } from 'react'; import { Address, GetContractReturnType, PublicClient, erc721Abi, getContract } from 'viem'; -import { useAccount, usePublicClient } from 'wagmi'; +import { useAccount } from 'wagmi'; import { useFractal } from '../../../providers/App/AppProvider'; import { useSafeAPI } from '../../../providers/App/hooks/useSafeAPI'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { AzoriusGovernance } from '../../../types'; +import useNetworkPublicClient from '../../useNetworkPublicClient'; import useVotingStrategiesAddresses from '../../utils/useVotingStrategiesAddresses'; /** @@ -35,7 +36,7 @@ export default function useUserERC721VotingTokens( const user = useAccount(); const { safe } = useDaoInfoStore(); const safeAPI = useSafeAPI(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const { getVotingStrategies } = useVotingStrategiesAddresses(); @@ -57,7 +58,7 @@ export default function useUserERC721VotingTokens( | GetContractReturnType | undefined; - if (!globalContextSafeAddress || !publicClient || !safeAPI) { + if (!globalContextSafeAddress || !safeAPI) { return { totalVotingTokenAddresses: totalTokenAddresses, totalVotingTokenIds: totalTokenIds, diff --git a/src/hooks/DAO/useDeployAzorius.ts b/src/hooks/DAO/useDeployAzorius.ts index 65a96caa9..d6e599611 100644 --- a/src/hooks/DAO/useDeployAzorius.ts +++ b/src/hooks/DAO/useDeployAzorius.ts @@ -4,7 +4,6 @@ import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; import { Address, encodeFunctionData, getContract, isHex } from 'viem'; -import { usePublicClient } from 'wagmi'; import GnosisSafeL2Abi from '../../assets/abi/GnosisSafeL2'; import MultiSendCallOnlyAbi from '../../assets/abi/MultiSendCallOnly'; import { SENTINEL_ADDRESS } from '../../constants/common'; @@ -21,6 +20,7 @@ import { SubDAO, VotingStrategyType, } from '../../types'; +import useNetworkPublicClient from '../useNetworkPublicClient'; import { useAddressContractType } from '../utils/useAddressContractType'; import { useCanUserCreateProposal } from '../utils/useCanUserSubmitProposal'; import { DecentModule } from './../../types/fractal'; @@ -58,7 +58,7 @@ const useDeployAzorius = () => { const { submitProposal } = useSubmitProposal(); const { canUserCreateProposal } = useCanUserCreateProposal(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const safeApi = useSafeAPI(); const lookupModules = useDecentModules(); @@ -89,7 +89,7 @@ const useDeployAzorius = () => { }, ) => { const { shouldSetName, shouldSetSnapshot } = opts; - if (!safeAddress || !canUserCreateProposal || !safe || !publicClient) { + if (!safeAddress || !canUserCreateProposal || !safe) { return; } diff --git a/src/hooks/DAO/useKeyValuePairs.ts b/src/hooks/DAO/useKeyValuePairs.ts index 97516c7eb..a0df8ab16 100644 --- a/src/hooks/DAO/useKeyValuePairs.ts +++ b/src/hooks/DAO/useKeyValuePairs.ts @@ -2,11 +2,11 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { hatIdToTreeId } from '@hatsprotocol/sdk-v1-core'; import { useEffect } from 'react'; import { Address, GetContractEventsReturnType, getContract } from 'viem'; -import { usePublicClient } from 'wagmi'; import { logError } from '../../helpers/errorLogging'; import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore'; import { useRolesStore } from '../../store/roles/useRolesStore'; +import useNetworkPublicClient from '../useNetworkPublicClient'; const getHatsTreeId = ( events: GetContractEventsReturnType | undefined, @@ -91,7 +91,7 @@ const getHatIdsToStreamIds = ( }; const useKeyValuePairs = () => { - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const node = useDaoInfoStore(); const { chain, @@ -102,7 +102,7 @@ const useKeyValuePairs = () => { const safeAddress = node.safe?.address; useEffect(() => { - if (!safeAddress || !publicClient) { + if (!safeAddress) { return; } diff --git a/src/hooks/utils/useAddress.ts b/src/hooks/utils/useAddress.ts index 56bed068e..a4ea33c22 100644 --- a/src/hooks/utils/useAddress.ts +++ b/src/hooks/utils/useAddress.ts @@ -1,10 +1,10 @@ import { useEffect, useState } from 'react'; import { Address, isAddress, getAddress } from 'viem'; import { normalize } from 'viem/ens'; -import { usePublicClient } from 'wagmi'; +import useNetworkPublicClient from '../useNetworkPublicClient'; const useAddress = (addressInput: string) => { - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const [address, setAddress] = useState
(); const [isValid, setIsValid] = useState(); const [isLoading, setIsLoading] = useState(false); @@ -29,13 +29,6 @@ const useAddress = (addressInput: string) => { return; } - if (!publicClient) { - setAddress(undefined); - setIsValid(undefined); - setIsLoading(false); - return; - } - let normalizedAddress: string; try { normalizedAddress = normalize(addressInput); @@ -45,7 +38,7 @@ const useAddress = (addressInput: string) => { setIsLoading(false); return; } - + // @todo this should be mainnet? publicClient .getEnsAddress({ name: normalizedAddress }) .then(resolvedAddress => { diff --git a/src/hooks/utils/useAddressContractType.ts b/src/hooks/utils/useAddressContractType.ts index 29350f45f..8256cac19 100644 --- a/src/hooks/utils/useAddressContractType.ts +++ b/src/hooks/utils/useAddressContractType.ts @@ -1,7 +1,7 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback } from 'react'; import { Abi, Address } from 'viem'; -import { usePublicClient } from 'wagmi'; +import useNetworkPublicClient from '../useNetworkPublicClient'; // https://github.com/adamgall/fractal-contract-identification/blob/229fc398661c5d684600feeb98a4eb767f728632/src/identify-contracts.ts @@ -220,14 +220,10 @@ const contractTests: ContractFunctionTest[] = [ ]; export function useAddressContractType() { - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const getAddressContractType = useCallback( async (address: Address): Promise => { - if (!publicClient) { - throw new Error('Public client not found'); - } - const result = { ...defaultContractType }; const allCalls = contractTests.flatMap(test => [ diff --git a/src/hooks/utils/useBlockTimestamp.ts b/src/hooks/utils/useBlockTimestamp.ts index 6875eeacd..8d4dc2f77 100644 --- a/src/hooks/utils/useBlockTimestamp.ts +++ b/src/hooks/utils/useBlockTimestamp.ts @@ -1,13 +1,13 @@ import { useEffect, useState } from 'react'; -import { usePublicClient } from 'wagmi'; import { logError } from '../../helpers/errorLogging'; +import useNetworkPublicClient from '../useNetworkPublicClient'; const useBlockTimestamp = (blockNumber?: number) => { - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const [timestamp, setTimestamp] = useState(Math.floor(Date.now() / 1000)); useEffect(() => { - if (!publicClient || !blockNumber) { + if (!blockNumber) { setTimestamp(Math.floor(Date.now() / 1000)); return; } diff --git a/src/hooks/utils/useCanUserSubmitProposal.ts b/src/hooks/utils/useCanUserSubmitProposal.ts index 5ed883c0a..64454d814 100644 --- a/src/hooks/utils/useCanUserSubmitProposal.ts +++ b/src/hooks/utils/useCanUserSubmitProposal.ts @@ -1,12 +1,13 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback, useEffect, useState } from 'react'; import { Address, getContract } from 'viem'; -import { useAccount, usePublicClient } from 'wagmi'; +import { useAccount } from 'wagmi'; import { useFractal } from '../../providers/App/AppProvider'; import { useSafeAPI } from '../../providers/App/hooks/useSafeAPI'; import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore'; import { GovernanceType } from '../../types'; import { isDemoMode } from '../../utils/demoMode'; +import useNetworkPublicClient from '../useNetworkPublicClient'; import useVotingStrategiesAddresses from './useVotingStrategiesAddresses'; export function useCanUserCreateProposal() { @@ -23,7 +24,7 @@ export function useCanUserCreateProposal() { const { safe } = useDaoInfoStore(); const safeAPI = useSafeAPI(); const [canUserCreateProposal, setCanUserCreateProposal] = useState(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const { getVotingStrategies } = useVotingStrategiesAddresses(); @@ -35,7 +36,7 @@ export function useCanUserCreateProposal() { */ const getCanUserCreateProposal = useCallback( async (safeAddress?: Address): Promise => { - if (!user.address || !safeAPI || !publicClient) { + if (!user.address || !safeAPI) { return; } diff --git a/src/hooks/utils/useCreateRoles.ts b/src/hooks/utils/useCreateRoles.ts index 6490f8349..1a4113156 100644 --- a/src/hooks/utils/useCreateRoles.ts +++ b/src/hooks/utils/useCreateRoles.ts @@ -23,7 +23,6 @@ import { parseAbiParameters, zeroAddress, } from 'viem'; -import { usePublicClient } from 'wagmi'; import GnosisSafeL2 from '../../assets/abi/GnosisSafeL2'; import { HatsAbi } from '../../assets/abi/HatsAbi'; import HatsAccount1ofNAbi from '../../assets/abi/HatsAccount1ofN'; @@ -56,6 +55,7 @@ import { SENTINEL_MODULE } from '../../utils/address'; import { prepareSendAssetsActionData } from '../../utils/dao/prepareSendAssetsActionData'; import useSubmitProposal from '../DAO/proposal/useSubmitProposal'; import useCreateSablierStream from '../streams/useCreateSablierStream'; +import useNetworkPublicClient from '../useNetworkPublicClient'; import { isElectionEligibilityModule, predictAccountAddress, @@ -120,12 +120,12 @@ export default function useCreateRoles() { const { prepareBatchLinearStreamCreation, prepareFlushStreamTxs, prepareCancelStreamTxs } = useCreateSablierStream(); const ipfsClient = useIPFSClient(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const navigate = useNavigate(); const buildDeployWhitelistingStrategy = useCallback( async (whitelistedHatsIds: bigint[]) => { - if (!publicClient || !safeAddress || !moduleAzoriusAddress) { + if (!safeAddress || !moduleAzoriusAddress) { return; } const azoriusGovernance = governance as AzoriusGovernance; @@ -477,10 +477,6 @@ export default function useCreateRoles() { const predictSmartAccount = useCallback( async (hatId: bigint) => { - if (!publicClient) { - throw new Error('Public client is not set'); - } - return predictAccountAddress({ implementation: hatsAccount1ofNMasterCopy, chainId: BigInt(chain.id), @@ -677,9 +673,6 @@ export default function useCreateRoles() { const isDecentAutonomousAdminV1 = useCallback( async (address: Address) => { - if (!publicClient) { - throw new Error('Public client is not set'); - } const decentAutonomousAdminV1Contract = getContract({ address: address, abi: abis.DecentAutonomousAdminV1, @@ -979,11 +972,10 @@ export default function useCreateRoles() { if (roleHatCurrentState.isTermed) { throw new Error('Cannot change role type for a termed role'); } - if (!hatsTree || !publicClient || !safeAddress) { + if (!hatsTree || !safeAddress) { throw new Error('App is not ready', { cause: { hatsTree, - publicClient, safeAddress, }, }); @@ -1196,10 +1188,6 @@ export default function useCreateRoles() { throw new Error('Cannot prepare transactions without hats tree or DAO address'); } - if (!publicClient) { - throw new Error('Cannot prepare transactions without public client'); - } - const topHatAccount = hatsTree.topHat.smartAddress; const adminHatWearer = hatsTree.adminHat.wearer; const allTxs: { calldata: Hex; targetAddress: Address }[] = []; @@ -1648,10 +1636,6 @@ export default function useCreateRoles() { const createEditRolesProposal = useCallback( async (values: RoleFormValues, formikHelpers: FormikHelpers) => { - if (!publicClient) { - throw new Error('Cannot create Roles proposal without public client'); - } - if (!safe) { throw new Error('Cannot create Roles proposal without known Safe'); } @@ -1742,7 +1726,6 @@ export default function useCreateRoles() { navigate, prepareCreateRolesModificationsProposalData, prepareCreateTopHatProposalData, - publicClient, safe, submitProposal, t, diff --git a/src/hooks/utils/useCurrentBlockNumber.ts b/src/hooks/utils/useCurrentBlockNumber.ts index 15f8e29ff..f6b7d0b8c 100644 --- a/src/hooks/utils/useCurrentBlockNumber.ts +++ b/src/hooks/utils/useCurrentBlockNumber.ts @@ -1,10 +1,10 @@ import { useEffect, useState, useCallback } from 'react'; -import { usePublicClient } from 'wagmi'; +import useNetworkPublicClient from '../useNetworkPublicClient'; const useCurrentBlockNumber = () => { const [currentBlockNumber, setCurrentBlockNumber] = useState(); const [isLoaded, setIsLoaded] = useState(false); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const updateBlockNumber = useCallback( (block: number) => { @@ -17,11 +17,6 @@ const useCurrentBlockNumber = () => { ); useEffect(() => { - if (!publicClient) { - setCurrentBlockNumber(undefined); - return; - } - const unwatch = publicClient.watchBlockNumber({ onBlockNumber: blockNumber => updateBlockNumber(Number(blockNumber)), }); diff --git a/src/hooks/utils/useSafeDecoder.tsx b/src/hooks/utils/useSafeDecoder.tsx index 2863092c0..9e538244e 100644 --- a/src/hooks/utils/useSafeDecoder.tsx +++ b/src/hooks/utils/useSafeDecoder.tsx @@ -2,17 +2,17 @@ import axios from 'axios'; import detectProxyTarget from 'evm-proxy-detection'; import { useCallback } from 'react'; import { Address, decodeFunctionData, encodePacked, Hex, keccak256 } from 'viem'; -import { usePublicClient } from 'wagmi'; import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { DecodedTransaction, DecodedTxParam } from '../../types'; import { buildSafeApiUrl, parseMultiSendTransactions } from '../../utils'; +import useNetworkPublicClient from '../useNetworkPublicClient'; import { CacheKeys } from './cache/cacheDefaults'; import { DBObjectKeys, useIndexedDB } from './cache/useLocalDB'; /** * Handles decoding and caching transactions via the Safe API. */ export const useSafeDecoder = () => { - const client = usePublicClient(); + const client = useNetworkPublicClient(); const { safeBaseURL, etherscanAPIUrl } = useNetworkConfigStore(); const [setValue, getValue] = useIndexedDB(DBObjectKeys.DECODED_TRANSACTIONS); const decode = useCallback( @@ -63,9 +63,6 @@ export const useSafeDecoder = () => { } } catch (e) { console.error('Error decoding transaction using Safe API. Trying to decode with ABI', e); - if (!client) { - throw new Error('Client not found'); - } const requestFunc = ({ method, params }: { method: any; params: any }) => client.request({ method, params }); const implementationAddress = await detectProxyTarget(to, requestFunc); diff --git a/src/hooks/utils/useSafeTransactions.ts b/src/hooks/utils/useSafeTransactions.ts index d39a94f5c..1f4561716 100644 --- a/src/hooks/utils/useSafeTransactions.ts +++ b/src/hooks/utils/useSafeTransactions.ts @@ -2,13 +2,13 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { SafeMultisigTransactionListResponse } from '@safe-global/api-kit'; import { useCallback } from 'react'; import { Address, getAddress, getContract } from 'viem'; -import { usePublicClient } from 'wagmi'; import { isApproved, isRejected } from '../../helpers/activity'; import { useFractal } from '../../providers/App/AppProvider'; import { FractalProposal, FractalProposalState } from '../../types'; import { parseDecodedData } from '../../utils'; import { getAverageBlockTime } from '../../utils/contract'; import { getTxTimelockedTimestamp } from '../../utils/guard'; +import useNetworkPublicClient from '../useNetworkPublicClient'; import { useSafeDecoder } from './useSafeDecoder'; type FreezeGuardData = { @@ -20,7 +20,7 @@ type FreezeGuardData = { export const useSafeTransactions = () => { const { guardContracts } = useFractal(); const decode = useSafeDecoder(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const getState = useCallback( async ( @@ -28,7 +28,7 @@ export const useSafeTransactions = () => { freezeGuardAddress?: Address, freezeGuardData?: FreezeGuardData, ) => { - if (freezeGuardAddress && freezeGuardData && publicClient) { + if (freezeGuardAddress && freezeGuardData) { return Promise.all( activities.map(async (activity, _, activityArr) => { if (!activity.transaction) { @@ -108,7 +108,7 @@ export const useSafeTransactions = () => { const parseTransactions = useCallback( async (transactions: SafeMultisigTransactionListResponse) => { - if (!transactions.results.length || !publicClient) { + if (!transactions.results.length) { return []; } @@ -170,7 +170,7 @@ export const useSafeTransactions = () => { ); let freezeGuardData: FreezeGuardData | undefined; - if (guardContracts.freezeGuardContractAddress && publicClient) { + if (guardContracts.freezeGuardContractAddress) { const blockNumber = await publicClient.getBlockNumber(); const averageBlockTime = BigInt(Math.round(await getAverageBlockTime(publicClient))); const freezeGuard = getContract({ diff --git a/src/hooks/utils/useVotingStrategiesAddresses.ts b/src/hooks/utils/useVotingStrategiesAddresses.ts index 9caafd065..d13fc6461 100644 --- a/src/hooks/utils/useVotingStrategiesAddresses.ts +++ b/src/hooks/utils/useVotingStrategiesAddresses.ts @@ -1,18 +1,18 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback } from 'react'; import { Address, getContract, zeroAddress } from 'viem'; -import { usePublicClient } from 'wagmi'; import { SENTINEL_ADDRESS } from '../../constants/common'; import { useSafeAPI } from '../../providers/App/hooks/useSafeAPI'; import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore'; import { DecentModule } from '../../types'; import { getAzoriusModuleFromModules } from '../../utils'; import { useDecentModules } from '../DAO/loaders/useDecentModules'; +import useNetworkPublicClient from '../useNetworkPublicClient'; import { useAddressContractType } from './useAddressContractType'; const useVotingStrategiesAddresses = () => { const node = useDaoInfoStore(); - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const safeAPI = useSafeAPI(); const { getAddressContractType } = useAddressContractType(); const lookupModules = useDecentModules(); @@ -35,7 +35,7 @@ const useVotingStrategiesAddresses = () => { azoriusModule = getAzoriusModuleFromModules(node.modules); } - if (!azoriusModule || !publicClient) { + if (!azoriusModule) { return; } diff --git a/src/pages/dao/proposals/new/sablier/SafeSablierProposalCreatePage.tsx b/src/pages/dao/proposals/new/sablier/SafeSablierProposalCreatePage.tsx index b986be625..fc30bab71 100644 --- a/src/pages/dao/proposals/new/sablier/SafeSablierProposalCreatePage.tsx +++ b/src/pages/dao/proposals/new/sablier/SafeSablierProposalCreatePage.tsx @@ -52,7 +52,6 @@ import { isAddress, zeroAddress, } from 'viem'; -import { usePublicClient } from 'wagmi'; import SablierV2BatchAbi from '../../../../../assets/abi/SablierV2Batch'; import { BigIntInput } from '../../../../../components/ui/forms/BigIntInput'; import ExampleLabel from '../../../../../components/ui/forms/ExampleLabel'; @@ -68,6 +67,7 @@ import CeleryButtonWithIcon from '../../../../../components/ui/utils/CeleryButto import { useHeaderHeight } from '../../../../../constants/common'; import { BASE_ROUTES, DAO_ROUTES } from '../../../../../constants/routes'; import useSubmitProposal from '../../../../../hooks/DAO/proposal/useSubmitProposal'; +import useNetworkPublicClient from '../../../../../hooks/useNetworkPublicClient'; import { useCanUserCreateProposal } from '../../../../../hooks/utils/useCanUserSubmitProposal'; import { analyticsEvents } from '../../../../../insights/analyticsEvents'; import { useFractal } from '../../../../../providers/App/AppProvider'; @@ -301,7 +301,7 @@ function StreamBuilder({ index: number; pendingTransaction: boolean; }) { - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const [tokenDecimals, setTokenDecimals] = useState(0); const [rawTokenBalance, setRawTokenBalnace] = useState(0n); const [tokenBalanceFormatted, setTokenBalanceFormatted] = useState(''); @@ -313,7 +313,7 @@ function StreamBuilder({ useEffect(() => { const fetchFormattedTokenBalance = async () => { - if (publicClient && safeAddress && stream.tokenAddress && isAddress(stream.tokenAddress)) { + if (safeAddress && stream.tokenAddress && isAddress(stream.tokenAddress)) { const tokenContract = getContract({ abi: erc20Abi, client: publicClient, diff --git a/src/pages/dao/settings/permissions/SafePermissionsCreateProposal.tsx b/src/pages/dao/settings/permissions/SafePermissionsCreateProposal.tsx index e541baf0c..d11b975bb 100644 --- a/src/pages/dao/settings/permissions/SafePermissionsCreateProposal.tsx +++ b/src/pages/dao/settings/permissions/SafePermissionsCreateProposal.tsx @@ -14,7 +14,6 @@ import { parseAbiParameters, zeroAddress, } from 'viem'; -import { usePublicClient } from 'wagmi'; import { SafePermissionsStrategyAction } from '../../../../components/SafeSettings/SafePermissionsStrategyAction'; import { SettingsPermissionsStrategyForm } from '../../../../components/SafeSettings/SettingsPermissionsStrategyForm'; import { Card } from '../../../../components/ui/cards/Card'; @@ -25,6 +24,7 @@ import NestedPageHeader from '../../../../components/ui/page/Header/NestedPageHe import Divider from '../../../../components/ui/utils/Divider'; import { DAO_ROUTES } from '../../../../constants/routes'; import { getRandomBytes } from '../../../../helpers'; +import useNetworkPublicClient from '../../../../hooks/useNetworkPublicClient'; import { generateContractByteCodeLinear } from '../../../../models/helpers/utils'; import { useFractal } from '../../../../providers/App/AppProvider'; import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore'; @@ -38,7 +38,7 @@ import { } from '../../../../types'; export function SafePermissionsCreateProposal() { - const publicClient = usePublicClient(); + const publicClient = useNetworkPublicClient(); const { t } = useTranslation(['settings', 'common', 'modals']); const { addressPrefix, @@ -98,7 +98,7 @@ export function SafePermissionsCreateProposal() { }; const handleCreateProposal = async () => { - if (proposerThreshold.bigintValue !== undefined && publicClient && moduleAzoriusAddress) { + if (proposerThreshold.bigintValue !== undefined && moduleAzoriusAddress) { let transactions: CreateProposalTransaction[]; let actionType: ProposalActionType = ProposalActionType.EDIT; From ae19faee375c93c09e585c2cb8703f167c154e63 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:44:52 -0500 Subject: [PATCH 04/13] Rename useWaletClient to useNetworkWalletClient and update chainId handling to use network configuration --- src/{useWaletClient.ts => hooks/useNetworkWalletClient.ts} | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) rename src/{useWaletClient.ts => hooks/useNetworkWalletClient.ts} (88%) diff --git a/src/useWaletClient.ts b/src/hooks/useNetworkWalletClient.ts similarity index 88% rename from src/useWaletClient.ts rename to src/hooks/useNetworkWalletClient.ts index c335f2555..7f03a11b3 100644 --- a/src/useWaletClient.ts +++ b/src/hooks/useNetworkWalletClient.ts @@ -10,6 +10,7 @@ import { SendTransactionRequest, } from 'viem'; import { useWalletClient } from 'wagmi'; +import { useNetworkConfigStore } from '../providers/NetworkConfig/useNetworkConfigStore'; type WalletClientExtension = { sendTransaction< @@ -28,7 +29,10 @@ type WalletClientExtension = { ): Promise; }; -export const useWaletClient = ({ chainId }: { chainId: number }) => { +export const useNetworkWalletClient = (props?: { chainId?: number }) => { + const { chain } = useNetworkConfigStore(); + const chainId = props?.chainId ?? chain.id; + const walletClient = useWalletClient({ chainId }); const extendedWalletClient = walletClient.data?.extend( From c1d3dc645184dee9e1b62f7dbd1afb8babb4719d Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:44:56 -0500 Subject: [PATCH 05/13] Refactor useDeployDAO to replace useWaletClient with useNetworkWalletClient --- src/hooks/DAO/useDeployDAO.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/hooks/DAO/useDeployDAO.ts b/src/hooks/DAO/useDeployDAO.ts index e42b5fab2..029fa8da3 100644 --- a/src/hooks/DAO/useDeployDAO.ts +++ b/src/hooks/DAO/useDeployDAO.ts @@ -4,7 +4,7 @@ import { Address, getContract, isHex } from 'viem'; import MultiSendCallOnlyAbi from '../../assets/abi/MultiSendCallOnly'; import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { AzoriusERC20DAO, AzoriusERC721DAO, SafeMultisigDAO } from '../../types'; -import { useWaletClient } from '../../useWaletClient'; +import { useNetworkWalletClient } from '../useNetworkWalletClient'; import { useTransaction } from '../utils/useTransaction'; import useBuildDAOTx from './useBuildDAOTx'; @@ -16,13 +16,10 @@ const useDeployDAO = () => { const { addressPrefix, - chain, contracts: { multiSendCallOnly }, } = useNetworkConfigStore(); - const { data: walletClient } = useWaletClient({ - chainId: chain.id, - }); + const { data: walletClient } = useNetworkWalletClient(); const deployDao = useCallback( ( From 3a79c690793f2980cf98d0312260ff6ce796a688 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:53:31 -0500 Subject: [PATCH 06/13] Replace useWalletClient with useNetworkWalletClient across multiple hooks and components --- src/components/DaoDashboard/ERC20Claim.tsx | 5 +++-- .../Proposals/MultisigProposalDetails/TxActions.tsx | 5 +++-- src/components/Roles/RoleTerm.tsx | 4 ++-- src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx | 4 ++-- src/components/ui/modals/DelegateModal.tsx | 5 +++-- src/components/ui/modals/PaymentWithdrawModal.tsx | 4 ++-- src/hooks/DAO/proposal/useCastSnapshotVote.ts | 4 ++-- src/hooks/DAO/proposal/useCastVote.ts | 4 ++-- src/hooks/DAO/proposal/useExecuteProposal.ts | 4 ++-- src/hooks/DAO/proposal/useSubmitProposal.ts | 5 +++-- src/hooks/DAO/useCastFreezeVote.ts | 4 ++-- src/hooks/utils/useApproval.tsx | 5 +++-- 12 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/components/DaoDashboard/ERC20Claim.tsx b/src/components/DaoDashboard/ERC20Claim.tsx index c01f8ec50..472d0c1b1 100644 --- a/src/components/DaoDashboard/ERC20Claim.tsx +++ b/src/components/DaoDashboard/ERC20Claim.tsx @@ -3,8 +3,9 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { getContract } from 'viem'; -import { useAccount, usePublicClient, useWalletClient } from 'wagmi'; +import { useAccount, usePublicClient } from 'wagmi'; import { Alert as AlertIcon } from '../../assets/theme/custom/icons/Alert'; +import { useNetworkWalletClient } from '../../hooks/useNetworkWalletClient'; import { useTransaction } from '../../hooks/utils/useTransaction'; import { useFractal } from '../../providers/App/AppProvider'; import { AzoriusGovernance } from '../../types'; @@ -20,7 +21,7 @@ export function ERCO20Claim() { const [contractCall, pending] = useTransaction(); const azoriusGovernance = governance as AzoriusGovernance; const publicClient = usePublicClient(); - const { data: walletClient } = useWalletClient(); + const { data: walletClient } = useNetworkWalletClient(); const loadClaim = useCallback(async () => { if (!tokenClaimContractAddress || !type || !account || !publicClient) { diff --git a/src/components/Proposals/MultisigProposalDetails/TxActions.tsx b/src/components/Proposals/MultisigProposalDetails/TxActions.tsx index a08e560a4..fcbc56337 100644 --- a/src/components/Proposals/MultisigProposalDetails/TxActions.tsx +++ b/src/components/Proposals/MultisigProposalDetails/TxActions.tsx @@ -3,13 +3,14 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { getAddress, getContract, isHex, zeroAddress } from 'viem'; -import { useAccount, useWalletClient } from 'wagmi'; +import { useAccount } from 'wagmi'; import GnosisSafeL2Abi from '../../../assets/abi/GnosisSafeL2'; import { Check } from '../../../assets/theme/custom/icons/Check'; import { BACKGROUND_SEMI_TRANSPARENT } from '../../../constants/common'; import { buildSafeTransaction, buildSignatureBytes, EIP712_SAFE_TX_TYPE } from '../../../helpers'; import { logError } from '../../../helpers/errorLogging'; import { useSafeMultisigProposals } from '../../../hooks/DAO/loaders/governance/useSafeMultisigProposals'; +import { useNetworkWalletClient } from '../../../hooks/useNetworkWalletClient'; import { useAsyncRequest } from '../../../hooks/utils/useAsyncRequest'; import { useTransaction } from '../../../hooks/utils/useTransaction'; import { useFractal } from '../../../providers/App/AppProvider'; @@ -48,7 +49,7 @@ export function TxActions({ proposal }: { proposal: MultisigProposal }) { const [asyncRequest, asyncRequestPending] = useAsyncRequest(); const [contractCall, contractCallPending] = useTransaction(); const { loadSafeMultisigProposals } = useSafeMultisigProposals(); - const { data: walletClient } = useWalletClient(); + const { data: walletClient } = useNetworkWalletClient(); const isOwner = safe?.owners?.includes(userAccount.address ?? zeroAddress); diff --git a/src/components/Roles/RoleTerm.tsx b/src/components/Roles/RoleTerm.tsx index 6b342afc6..66342e555 100644 --- a/src/components/Roles/RoleTerm.tsx +++ b/src/components/Roles/RoleTerm.tsx @@ -5,9 +5,9 @@ import { format } from 'date-fns'; import { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Address, getContract, Hex } from 'viem'; -import { useWalletClient } from 'wagmi'; import { DETAILS_BOX_SHADOW } from '../../constants/common'; import { useDateTimeDisplay } from '../../helpers/dateTime'; +import { useNetworkWalletClient } from '../../hooks/useNetworkWalletClient'; import useAvatar from '../../hooks/utils/useAvatar'; import { useCopyText } from '../../hooks/utils/useCopyText'; import { useGetAccountName } from '../../hooks/utils/useGetAccountName'; @@ -280,7 +280,7 @@ export default function RoleTerm({ }) { const [contractCall, contractCallPending] = useTransaction(); const { hatsTree, getHat, updateCurrentTermStatus } = useRolesStore(); - const { data: walletClient } = useWalletClient(); + const { data: walletClient } = useNetworkWalletClient(); const { t } = useTranslation(['roles']); const { contracts: { hatsProtocol }, diff --git a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx index 72a807406..963dd53a6 100644 --- a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx +++ b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx @@ -4,7 +4,6 @@ import { GearFine } from '@phosphor-icons/react'; import { useCallback, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import { getContract } from 'viem'; -import { useWalletClient } from 'wagmi'; import { DAO_ROUTES } from '../../../../constants/routes'; import { isWithinFreezePeriod, @@ -12,6 +11,7 @@ import { } from '../../../../helpers/freezePeriodHelpers'; import useUserERC721VotingTokens from '../../../../hooks/DAO/proposal/useUserERC721VotingTokens'; import useClawBack from '../../../../hooks/DAO/useClawBack'; +import { useNetworkWalletClient } from '../../../../hooks/useNetworkWalletClient'; import useBlockTimestamp from '../../../../hooks/utils/useBlockTimestamp'; import { useCanUserCreateProposal } from '../../../../hooks/utils/useCanUserSubmitProposal'; import { useFractal } from '../../../../providers/App/AppProvider'; @@ -52,7 +52,7 @@ export function ManageDAOMenu() { const handleModifyGovernance = useDecentModal(ModalType.CONFIRM_MODIFY_GOVERNANCE); - const { data: walletClient } = useWalletClient(); + const { data: walletClient } = useNetworkWalletClient(); const freezeOption = useMemo( () => ({ diff --git a/src/components/ui/modals/DelegateModal.tsx b/src/components/ui/modals/DelegateModal.tsx index d26f570f0..7bcc6b3ed 100644 --- a/src/components/ui/modals/DelegateModal.tsx +++ b/src/components/ui/modals/DelegateModal.tsx @@ -3,10 +3,11 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { Field, FieldAttributes, Formik } from 'formik'; import { useTranslation } from 'react-i18next'; import { getAddress, getContract, zeroAddress } from 'viem'; -import { useAccount, usePublicClient, useWalletClient } from 'wagmi'; +import { useAccount, usePublicClient } from 'wagmi'; import * as Yup from 'yup'; import LockReleaseAbi from '../../../assets/abi/LockRelease'; import { useValidationAddress } from '../../../hooks/schemas/common/useValidationAddress'; +import { useNetworkWalletClient } from '../../../hooks/useNetworkWalletClient'; import { useGetAccountName } from '../../../hooks/utils/useGetAccountName'; import { useTransaction } from '../../../hooks/utils/useTransaction'; import { useFractal } from '../../../providers/App/AppProvider'; @@ -35,7 +36,7 @@ export function DelegateModal({ close }: { close: Function }) { ); const [contractCall, pending] = useTransaction(); const { addressValidationTest } = useValidationAddress(); - const { data: walletClient } = useWalletClient(); + const { data: walletClient } = useNetworkWalletClient(); const publicClient = usePublicClient(); const submitDelegation = async (values: { address: string }) => { diff --git a/src/components/ui/modals/PaymentWithdrawModal.tsx b/src/components/ui/modals/PaymentWithdrawModal.tsx index 3cacfa8bb..a5cc1163c 100644 --- a/src/components/ui/modals/PaymentWithdrawModal.tsx +++ b/src/components/ui/modals/PaymentWithdrawModal.tsx @@ -3,10 +3,10 @@ import { Download } from '@phosphor-icons/react'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { Address, encodeFunctionData, getContract } from 'viem'; -import { useWalletClient } from 'wagmi'; import HatsAccount1ofNAbi from '../../../assets/abi/HatsAccount1ofN'; import { SablierV2LockupLinearAbi } from '../../../assets/abi/SablierV2LockupLinear'; import { convertStreamIdToBigInt } from '../../../hooks/streams/useCreateSablierStream'; +import { useNetworkWalletClient } from '../../../hooks/useNetworkWalletClient'; import useAvatar from '../../../hooks/utils/useAvatar'; import { useGetAccountName } from '../../../hooks/utils/useGetAccountName'; import { useTransaction } from '../../../hooks/utils/useTransaction'; @@ -38,7 +38,7 @@ export function PaymentWithdrawModal({ onSuccess: () => Promise; onClose: () => void; }) { - const { data: walletClient } = useWalletClient(); + const { data: walletClient } = useNetworkWalletClient(); const [contractCall, pendingTransaction] = useTransaction(); const { t } = useTranslation(['roles', 'menu', 'common', 'modals']); const { displayName: accountDisplayName } = useGetAccountName(withdrawInformation.recipient); diff --git a/src/hooks/DAO/proposal/useCastSnapshotVote.ts b/src/hooks/DAO/proposal/useCastSnapshotVote.ts index d7a26b1c1..edb162d84 100644 --- a/src/hooks/DAO/proposal/useCastSnapshotVote.ts +++ b/src/hooks/DAO/proposal/useCastSnapshotVote.ts @@ -1,12 +1,12 @@ import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { toast } from 'sonner'; -import { useWalletClient } from 'wagmi'; import { logError } from '../../../helpers/errorLogging'; import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { ExtendedSnapshotProposal } from '../../../types'; import encryptWithShutter from '../../../utils/shutter'; import { submitSnapshotVote } from '../../../utils/snapshotVote'; +import { useNetworkWalletClient } from '../../useNetworkWalletClient'; const useCastSnapshotVote = (extendedSnapshotProposal: ExtendedSnapshotProposal | null) => { const [selectedChoice, setSelectedChoice] = useState(); @@ -14,7 +14,7 @@ const useCastSnapshotVote = (extendedSnapshotProposal: ExtendedSnapshotProposal const { subgraphInfo } = useDaoInfoStore(); - const { data: walletClient } = useWalletClient(); + const { data: walletClient } = useNetworkWalletClient(); useEffect(() => { if (extendedSnapshotProposal) { diff --git a/src/hooks/DAO/proposal/useCastVote.ts b/src/hooks/DAO/proposal/useCastVote.ts index fcc733a0b..43c019ebe 100644 --- a/src/hooks/DAO/proposal/useCastVote.ts +++ b/src/hooks/DAO/proposal/useCastVote.ts @@ -2,8 +2,8 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { Address, getContract } from 'viem'; -import { useWalletClient } from 'wagmi'; import { useFractal } from '../../../providers/App/AppProvider'; +import { useNetworkWalletClient } from '../../useNetworkWalletClient'; import { useTransaction } from '../../utils/useTransaction'; import useUserERC721VotingTokens from './useUserERC721VotingTokens'; @@ -24,7 +24,7 @@ const useCastVote = (proposalId: string, strategy: Address) => { proposalId, ); - const { data: walletClient } = useWalletClient(); + const { data: walletClient } = useNetworkWalletClient(); const { t } = useTranslation('transaction'); diff --git a/src/hooks/DAO/proposal/useExecuteProposal.ts b/src/hooks/DAO/proposal/useExecuteProposal.ts index 705512d43..ea6778dcf 100644 --- a/src/hooks/DAO/proposal/useExecuteProposal.ts +++ b/src/hooks/DAO/proposal/useExecuteProposal.ts @@ -2,9 +2,9 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { Address, Hex, getContract } from 'viem'; -import { useWalletClient } from 'wagmi'; import { useFractal } from '../../../providers/App/AppProvider'; import { MetaTransaction, FractalProposal, AzoriusProposal } from '../../../types'; +import { useNetworkWalletClient } from '../../useNetworkWalletClient'; import { useTransaction } from '../../utils/useTransaction'; import useUpdateProposalState from './useUpdateProposalState'; @@ -17,7 +17,7 @@ export default function useExecuteProposal() { governanceContracts, governanceDispatch: action.dispatch, }); - const { data: walletClient } = useWalletClient(); + const { data: walletClient } = useNetworkWalletClient(); const [contractCall, pending] = useTransaction(); const executeProposal = useCallback( diff --git a/src/hooks/DAO/proposal/useSubmitProposal.ts b/src/hooks/DAO/proposal/useSubmitProposal.ts index 64a33cb4f..a7646ba1d 100644 --- a/src/hooks/DAO/proposal/useSubmitProposal.ts +++ b/src/hooks/DAO/proposal/useSubmitProposal.ts @@ -12,7 +12,7 @@ import { isHex, parseAbiParameters, } from 'viem'; -import { useAccount, usePublicClient, useWalletClient } from 'wagmi'; +import { useAccount, usePublicClient } from 'wagmi'; import MultiSendCallOnlyAbi from '../../../assets/abi/MultiSendCallOnly'; import { ADDRESS_MULTISIG_METADATA } from '../../../constants/common'; import { buildSafeAPIPost, encodeMultiSend } from '../../../helpers'; @@ -25,6 +25,7 @@ import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetwo import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore'; import { CreateProposalMetadata, MetaTransaction, ProposalExecuteData } from '../../../types'; import { buildSafeApiUrl, getAzoriusModuleFromModules } from '../../../utils'; +import { useNetworkWalletClient } from '../../useNetworkWalletClient'; import useVotingStrategiesAddresses from '../../utils/useVotingStrategiesAddresses'; import { useDecentModules } from '../loaders/useDecentModules'; import { useLoadDAOProposals } from '../loaders/useLoadDAOProposals'; @@ -61,7 +62,7 @@ export default function useSubmitProposal() { const { t } = useTranslation('proposal'); const [pendingCreateTx, setPendingCreateTx] = useState(false); const loadDAOProposals = useLoadDAOProposals(); - const { data: walletClient } = useWalletClient(); + const { data: walletClient } = useNetworkWalletClient(); const publicClient = usePublicClient(); const { getVotingStrategies } = useVotingStrategiesAddresses(); diff --git a/src/hooks/DAO/useCastFreezeVote.ts b/src/hooks/DAO/useCastFreezeVote.ts index 7da10c5e1..974ccaffc 100644 --- a/src/hooks/DAO/useCastFreezeVote.ts +++ b/src/hooks/DAO/useCastFreezeVote.ts @@ -2,10 +2,10 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { getContract } from 'viem'; -import { useWalletClient } from 'wagmi'; import { useFractal } from '../../providers/App/AppProvider'; import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore'; import { FreezeVotingType } from '../../types'; +import { useNetworkWalletClient } from '../useNetworkWalletClient'; import { useTransaction } from '../utils/useTransaction'; import useUserERC721VotingTokens from './proposal/useUserERC721VotingTokens'; @@ -18,7 +18,7 @@ export const useCastFreezeVote = () => { const { getUserERC721VotingTokens } = useUserERC721VotingTokens(null, null, false); const { t } = useTranslation('transaction'); - const { data: walletClient } = useWalletClient(); + const { data: walletClient } = useNetworkWalletClient(); const castFreezeVote = useCallback(() => { if (!freezeVotingContractAddress) return; diff --git a/src/hooks/utils/useApproval.tsx b/src/hooks/utils/useApproval.tsx index 1e88801a8..36d0c5a7d 100644 --- a/src/hooks/utils/useApproval.tsx +++ b/src/hooks/utils/useApproval.tsx @@ -2,7 +2,8 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Address, getContract, maxUint256 } from 'viem'; -import { useAccount, useWalletClient } from 'wagmi'; +import { useAccount } from 'wagmi'; +import { useNetworkWalletClient } from '../useNetworkWalletClient'; import { useTransaction } from './useTransaction'; const useApproval = (tokenAddress?: Address, spenderAddress?: Address, userBalance?: bigint) => { @@ -11,7 +12,7 @@ const useApproval = (tokenAddress?: Address, spenderAddress?: Address, userBalan const [approved, setApproved] = useState(false); const [contractCall, pending] = useTransaction(); const { t } = useTranslation('treasury'); - const { data: walletClient } = useWalletClient(); + const { data: walletClient } = useNetworkWalletClient(); const tokenContract = useMemo(() => { if (!walletClient || !tokenAddress) { From 249e6b5ec5aa6a5c4cdf126d8391815c2858c216 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 16 Jan 2025 23:13:59 -0500 Subject: [PATCH 07/13] Refactor useAutomaticSwitchChain to remove wallet client dependencies and simplify chain switching logic --- src/hooks/utils/useAutomaticSwitchChain.ts | 30 +++------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/src/hooks/utils/useAutomaticSwitchChain.ts b/src/hooks/utils/useAutomaticSwitchChain.ts index 72fa52af3..b7cdce58e 100644 --- a/src/hooks/utils/useAutomaticSwitchChain.ts +++ b/src/hooks/utils/useAutomaticSwitchChain.ts @@ -1,5 +1,4 @@ import { useEffect } from 'react'; -import { useChainId, useSwitchChain, useWalletClient } from 'wagmi'; import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; import { getChainIdFromPrefix } from '../../utils/url'; @@ -9,35 +8,12 @@ export const useAutomaticSwitchChain = ({ urlAddressPrefix: string | undefined; }) => { const { setCurrentConfig, getConfigByChainId, addressPrefix } = useNetworkConfigStore(); - const { isFetchedAfterMount } = useWalletClient(); - const walletChainId = useChainId(); - const { switchChain } = useSwitchChain({ - mutation: { - onError: () => { - if (addressPrefix !== urlAddressPrefix && urlAddressPrefix !== undefined) { - const chainId = getChainIdFromPrefix(urlAddressPrefix); - switchChain({ chainId }); - } - }, - }, - }); useEffect(() => { if (urlAddressPrefix === undefined) { return; } - const chainId = getChainIdFromPrefix(urlAddressPrefix); - if ((addressPrefix !== urlAddressPrefix || chainId !== walletChainId) && isFetchedAfterMount) { - switchChain({ chainId }); - } - setTimeout(() => setCurrentConfig(getConfigByChainId(chainId)), 300); - }, [ - addressPrefix, - setCurrentConfig, - getConfigByChainId, - urlAddressPrefix, - walletChainId, - switchChain, - isFetchedAfterMount, - ]); + + setCurrentConfig(getConfigByChainId(getChainIdFromPrefix(urlAddressPrefix))); + }, [addressPrefix, setCurrentConfig, getConfigByChainId, urlAddressPrefix]); }; From 90ab26d12520ee0e72b4169e5031a096f1ae060f Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 16 Jan 2025 23:15:53 -0500 Subject: [PATCH 08/13] Update ABISelector and useGetAccountName to utilize chainId from network configuration --- src/components/ui/forms/ABISelector.tsx | 4 ++-- src/hooks/utils/useGetAccountName.ts | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/ui/forms/ABISelector.tsx b/src/components/ui/forms/ABISelector.tsx index 07f1f6dc9..a2943650f 100644 --- a/src/components/ui/forms/ABISelector.tsx +++ b/src/components/ui/forms/ABISelector.tsx @@ -27,9 +27,9 @@ interface IABISelector { export default function ABISelector({ target, onChange }: IABISelector) { const [abi, setABI] = useState([]); - const { etherscanAPIUrl } = useNetworkConfigStore(); + const { etherscanAPIUrl, chain } = useNetworkConfigStore(); const { t } = useTranslation('common'); - const { data: ensAddress } = useEnsAddress({ name: target?.toLowerCase() }); + const { data: ensAddress } = useEnsAddress({ name: target?.toLowerCase(), chainId: chain.id }); const client = useNetworkPublicClient(); useEffect(() => { diff --git a/src/hooks/utils/useGetAccountName.ts b/src/hooks/utils/useGetAccountName.ts index c2b22282a..1c2135b9f 100644 --- a/src/hooks/utils/useGetAccountName.ts +++ b/src/hooks/utils/useGetAccountName.ts @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react'; import { Address } from 'viem'; import { useEnsName } from 'wagmi'; +import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore'; export const createAccountSubstring = (account: string) => { return `${account.substring(0, 6)}...${account.slice(-4)}`; @@ -16,10 +17,12 @@ export const createAccountSubstring = (account: string) => { * @todo Should switch to object for props */ export const useGetAccountName = (account?: Address | null, truncate?: boolean) => { + const { chain } = useNetworkConfigStore(); if (truncate === undefined) truncate = true; const { data: ensName } = useEnsName({ address: !!account ? account : undefined, + chainId: chain.id, }); const [accountSubstring, setAccountSubstring] = useState(); From 202af38be3650535c73ba28898860f45821074e5 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 16 Jan 2025 23:33:19 -0500 Subject: [PATCH 09/13] Update multicall configuration in useNetworkPublicClient to include a wait time --- src/hooks/useNetworkPublicClient.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hooks/useNetworkPublicClient.ts b/src/hooks/useNetworkPublicClient.ts index 8f553f52a..1ecd7836d 100644 --- a/src/hooks/useNetworkPublicClient.ts +++ b/src/hooks/useNetworkPublicClient.ts @@ -9,7 +9,9 @@ export default function useNetworkPublicClient() { createPublicClient({ chain, batch: { - multicall: true, + multicall: { + wait: 500, + }, }, transport: http(rpcEndpoint), }), From 39d3000e763af45e24cbf646d6f626b55b14d6be Mon Sep 17 00:00:00 2001 From: Adam Gall Date: Fri, 17 Jan 2025 02:39:39 -0500 Subject: [PATCH 10/13] bump From 329f905d827356da928152146a8d7f885839f196 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 17 Jan 2025 15:19:04 -0500 Subject: [PATCH 11/13] Remove publicClient checks from various components and hooks to simplify address validation logic --- .../DaoCreator/formComponents/AzoriusNFTDetail.tsx | 2 +- .../DaoCreator/formComponents/AzoriusTokenDetails.tsx | 3 --- src/hooks/DAO/useBuildDAOTx.ts | 2 +- src/hooks/schemas/DAOCreate/useDAOCreateTests.ts | 4 ++-- src/hooks/schemas/common/useValidationAddress.tsx | 10 +++++----- src/hooks/utils/useTransaction.ts | 2 -- src/store/roles/rolesStoreUtils.ts | 2 +- 7 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/components/DaoCreator/formComponents/AzoriusNFTDetail.tsx b/src/components/DaoCreator/formComponents/AzoriusNFTDetail.tsx index fe9f45c74..ebde78b22 100644 --- a/src/components/DaoCreator/formComponents/AzoriusNFTDetail.tsx +++ b/src/components/DaoCreator/formComponents/AzoriusNFTDetail.tsx @@ -30,7 +30,7 @@ export default function AzoriusNFTDetail({ useEffect(() => { const loadNFTDetails = async () => { - if (hasAddressError || !publicClient) { + if (hasAddressError) { return; } diff --git a/src/components/DaoCreator/formComponents/AzoriusTokenDetails.tsx b/src/components/DaoCreator/formComponents/AzoriusTokenDetails.tsx index 889d96fd1..f9428a2b0 100644 --- a/src/components/DaoCreator/formComponents/AzoriusTokenDetails.tsx +++ b/src/components/DaoCreator/formComponents/AzoriusTokenDetails.tsx @@ -42,9 +42,6 @@ export function AzoriusTokenDetails(props: ICreationStepProps) { useStepRedirect({ values }); const updateImportFields = useCallback(async () => { - if (!publicClient) { - return; - } const importAddress = values.erc20Token.tokenImportAddress; const importError = errors?.erc20Token?.tokenImportAddress; if (importAddress && !importError && isAddress(importAddress)) { diff --git a/src/hooks/DAO/useBuildDAOTx.ts b/src/hooks/DAO/useBuildDAOTx.ts index 7cf6c039b..666a09804 100644 --- a/src/hooks/DAO/useBuildDAOTx.ts +++ b/src/hooks/DAO/useBuildDAOTx.ts @@ -54,7 +54,7 @@ const useBuildDAOTx = () => { ) => { let isAzorius = false; - if (!user.address || !publicClient) { + if (!user.address) { return; } diff --git a/src/hooks/schemas/DAOCreate/useDAOCreateTests.ts b/src/hooks/schemas/DAOCreate/useDAOCreateTests.ts index 95d15be1f..d3807970f 100644 --- a/src/hooks/schemas/DAOCreate/useDAOCreateTests.ts +++ b/src/hooks/schemas/DAOCreate/useDAOCreateTests.ts @@ -45,7 +45,7 @@ export function useDAOCreateTests() { name: 'Address Validation', message: t('errorInvalidENSAddress', { ns: 'common', chain: chain.name }), test: async function (address: string | undefined) { - if (!address || !publicClient) return false; + if (!address) return false; const { validation } = await validateAddress({ publicClient, address }); if (validation.isValidAddress) { addressValidationMap.current.set(address, validation); @@ -60,7 +60,7 @@ export function useDAOCreateTests() { name: 'Unique Addresses', message: t('errorDuplicateAddress'), test: async function (value: string | undefined, context: AnyObject) { - if (!value || !publicClient) return false; + if (!value) return false; // retreive parent array const parentAddressArray = context.from[1].value.tokenAllocations; if (parentAddressArray.length === 1) { diff --git a/src/hooks/schemas/common/useValidationAddress.tsx b/src/hooks/schemas/common/useValidationAddress.tsx index cda782fbb..3699d7770 100644 --- a/src/hooks/schemas/common/useValidationAddress.tsx +++ b/src/hooks/schemas/common/useValidationAddress.tsx @@ -84,7 +84,7 @@ export const useValidationAddress = () => { name: 'Address Validation', message: t('errorInvalidENSAddress', { ns: 'common', chain: chain.name }), test: async function (address: string | undefined) { - if (!address || !publicClient) return false; + if (!address) return false; setIsValidating(true); try { @@ -119,7 +119,7 @@ export const useValidationAddress = () => { name: 'Address Validation', message: t('errorInvalidAddress', { ns: 'common' }), test: async function (address: string | undefined) { - if (!address || !publicClient) return false; + if (!address) return false; setIsValidating(true); const { validation } = await validateAddress({ publicClient, @@ -140,7 +140,7 @@ export const useValidationAddress = () => { name: 'New Signer Validation', message: t('alreadySigner', { ns: 'modals' }), test: async function (addressOrENS: string | undefined) { - if (!addressOrENS || !safe || !publicClient) return false; + if (!addressOrENS || !safe) return false; let resolvedAddress: Address | null; @@ -163,7 +163,7 @@ export const useValidationAddress = () => { async (value: string, addressArray: string[]) => { // looks up tested value let inputValidation = addressValidationMap.current.get(value); - if (!!value && !inputValidation && publicClient) { + if (!!value && !inputValidation) { inputValidation = (await validateAddress({ publicClient, address: value })).validation; } // converts all inputs to addresses to compare @@ -176,7 +176,7 @@ export const useValidationAddress = () => { return addressValidation.address; } // because mapping is not 'state', this catches values that may not be resolved yet - if (normalize(address) && publicClient) { + if (normalize(address)) { const { validation } = await validateAddress({ publicClient, address }); return validation.address; } diff --git a/src/hooks/utils/useTransaction.ts b/src/hooks/utils/useTransaction.ts index ec19c4339..d8a0921f0 100644 --- a/src/hooks/utils/useTransaction.ts +++ b/src/hooks/utils/useTransaction.ts @@ -28,8 +28,6 @@ const useTransaction = () => { const contractCall = useCallback( (params: ContractCallParams) => { - if (!publicClient) return; - let toastId = toast.loading(params.pendingMessage, { duration: Infinity, }); diff --git a/src/store/roles/rolesStoreUtils.ts b/src/store/roles/rolesStoreUtils.ts index 43ebf9f77..fce0a685b 100644 --- a/src/store/roles/rolesStoreUtils.ts +++ b/src/store/roles/rolesStoreUtils.ts @@ -274,7 +274,7 @@ const getPaymentStreams = async ( slug: string; }, ): Promise => { - if (!sablierSubgraph || !publicClient) { + if (!sablierSubgraph) { return []; } const streamQueryResult = await apolloClient.query({ From ffe9f243e7e090eca2aa056875a94d46266fb010 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Mon, 20 Jan 2025 10:29:35 -0500 Subject: [PATCH 12/13] Reduce wait time in multicall configuration for useNetworkPublicClient --- src/hooks/useNetworkPublicClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useNetworkPublicClient.ts b/src/hooks/useNetworkPublicClient.ts index 1ecd7836d..5410aaeeb 100644 --- a/src/hooks/useNetworkPublicClient.ts +++ b/src/hooks/useNetworkPublicClient.ts @@ -10,7 +10,7 @@ export default function useNetworkPublicClient() { chain, batch: { multicall: { - wait: 500, + wait: 200, }, }, transport: http(rpcEndpoint), From e2fb3635df6d9903a00e36d8e184c578aff79b55 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Wed, 22 Jan 2025 01:35:46 -0500 Subject: [PATCH 13/13] catch and display, wrong network toast, when fails to switch --- src/hooks/useNetworkWalletClient.ts | 14 ++++++++++++-- src/hooks/utils/useTransaction.ts | 4 ++++ src/i18n/locales/en/common.json | 1 + 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/hooks/useNetworkWalletClient.ts b/src/hooks/useNetworkWalletClient.ts index 7f03a11b3..834a7581c 100644 --- a/src/hooks/useNetworkWalletClient.ts +++ b/src/hooks/useNetworkWalletClient.ts @@ -1,3 +1,4 @@ +import { useTranslation } from 'react-i18next'; import { Account, Chain, @@ -31,6 +32,7 @@ type WalletClientExtension = { export const useNetworkWalletClient = (props?: { chainId?: number }) => { const { chain } = useNetworkConfigStore(); + const { t } = useTranslation('common'); const chainId = props?.chainId ?? chain.id; const walletClient = useWalletClient({ chainId }); @@ -41,7 +43,11 @@ export const useNetworkWalletClient = (props?: { chainId?: number }) => { TRequest extends SendTransactionRequest, TChainOverride extends Chain | undefined = undefined, >(args: SendTransactionParameters) { - await client.switchChain({ id: chainId }); + try { + await client.switchChain({ id: chainId }); + } catch (e) { + throw new Error(t('wrongNetwork')); + } return client.sendTransaction(args); }, @@ -51,7 +57,11 @@ export const useNetworkWalletClient = (props?: { chainId?: number }) => { TArgs extends ContractFunctionArgs, TChain extends Chain | undefined = Chain, >(args: WriteContractParameters) { - await client.switchChain({ id: chainId }); + try { + await client.switchChain({ id: chainId }); + } catch (e) { + throw new Error(t('wrongNetwork')); + } return client.writeContract(args); }, }), diff --git a/src/hooks/utils/useTransaction.ts b/src/hooks/utils/useTransaction.ts index d8a0921f0..1c2cbf24b 100644 --- a/src/hooks/utils/useTransaction.ts +++ b/src/hooks/utils/useTransaction.ts @@ -63,6 +63,10 @@ const useTransaction = () => { toast.info(t('errorUserDeniedTransaction', { id: toastId })); return; } + if (error.message === t('wrongNetwork', { ns: 'common' })) { + toast.error(error.message, { id: toastId }); + return; + } toast.error(t('errorGeneral', { ns: 'common' }), { id: toastId }); }); diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index 96d5bed3e..557f3ba6c 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -102,6 +102,7 @@ "refresh": "Refresh", "invalidSafe1": "We couldn't find a Safe at this address on {{chain}}.", "invalidSafe2": "Please double check the address then try again.", + "wrongNetwork": "There was an error switching the network of the wallet", "wrongNetwork1": "You are currently connected to a different network than this Safe.", "wrongNetwork2": "Please switch to the correct network for your Safe.", "badQueryParam1": "The Safe address in the URL isn't formatted correctly.",