From db4b339894778e2bf0e07f34ab05aa58ed118912 Mon Sep 17 00:00:00 2001 From: Adam Gall Date: Wed, 29 May 2024 09:03:34 -0400 Subject: [PATCH] useVotingStrategyAddress now returns function which accepts optional safe address --- .../ui/menus/ManageDAO/ManageDAOMenu.tsx | 11 +++-- .../DAO/loaders/useGovernanceContracts.ts | 23 +++++------ src/hooks/DAO/proposal/useSubmitProposal.ts | 12 +++--- .../DAO/proposal/useUserERC721VotingTokens.ts | 10 ++--- src/hooks/utils/useCanUserSubmitProposal.ts | 9 ++-- src/hooks/utils/useVotingStrategyAddress.ts | 41 +++++++++++++------ 6 files changed, 61 insertions(+), 45 deletions(-) diff --git a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx index cec85b4e75..203c49a3c4 100644 --- a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx +++ b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx @@ -3,7 +3,7 @@ import { ERC20FreezeVoting, MultisigFreezeVoting } from '@fractal-framework/frac import { GearFine } from '@phosphor-icons/react'; import { useMemo, useCallback, useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import { Address, getAddress } from 'viem'; +import { Address } from 'viem'; import { DAO_ROUTES } from '../../../../constants/routes'; import { isWithinFreezePeriod, @@ -60,7 +60,7 @@ export function ManageDAOMenu({ parentAddress, freezeGuard, guardContracts }: IM parentAddress, childSafeInfo: node, }); - const votingContractAddress = useVotingStrategyAddress(); + const { getVotingStrategyAddress } = useVotingStrategyAddress(); useEffect(() => { const loadGovernanceType = async () => { @@ -71,10 +71,9 @@ export function ManageDAOMenu({ parentAddress, freezeGuard, guardContracts }: IM } else { if (node?.fractalModules) { let result = GovernanceType.MULTISIG; + const votingContractAddress = await getVotingStrategyAddress(); if (votingContractAddress) { - const masterCopyData = await getZodiacModuleProxyMasterCopyData( - getAddress(votingContractAddress), - ); + const masterCopyData = await getZodiacModuleProxyMasterCopyData(votingContractAddress); if (masterCopyData.isOzLinearVoting) { result = GovernanceType.AZORIUS_ERC20; @@ -90,12 +89,12 @@ export function ManageDAOMenu({ parentAddress, freezeGuard, guardContracts }: IM loadGovernanceType(); }, [ + getVotingStrategyAddress, getZodiacModuleProxyMasterCopyData, node?.fractalModules, node.safe, safeAddress, type, - votingContractAddress, ]); const { addressPrefix } = useNetworkConfig(); diff --git a/src/hooks/DAO/loaders/useGovernanceContracts.ts b/src/hooks/DAO/loaders/useGovernanceContracts.ts index 9962256758..d3c8d17614 100644 --- a/src/hooks/DAO/loaders/useGovernanceContracts.ts +++ b/src/hooks/DAO/loaders/useGovernanceContracts.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useRef } from 'react'; -import { getContract, getAddress } from 'viem'; +import { getContract, Address } from 'viem'; import { usePublicClient } from 'wagmi'; import LinearERC20VotingAbi from '../../../assets/abi/LinearERC20Voting'; import LockReleaseAbi from '../../../assets/abi/LockRelease'; @@ -19,7 +19,7 @@ export const useGovernanceContracts = () => { const { getZodiacModuleProxyMasterCopyData } = useMasterCopy(); const publicClient = usePublicClient(); - const votingStrategyAddress = useVotingStrategyAddress(); + const { getVotingStrategyAddress } = useVotingStrategyAddress(); const { fractalModules, isModulesLoaded, daoAddress } = node; @@ -29,6 +29,8 @@ export const useGovernanceContracts = () => { } const azoriusModule = getAzoriusModuleFromModules(fractalModules); + const votingStrategyAddress = await getVotingStrategyAddress(); + if (!azoriusModule || !votingStrategyAddress) { action.dispatch({ type: GovernanceContractAction.SET_GOVERNANCE_CONTRACT_ADDRESSES, @@ -37,31 +39,28 @@ export const useGovernanceContracts = () => { return; } - let ozLinearVotingContractAddress: string | undefined; + let ozLinearVotingContractAddress: Address | undefined; let erc721LinearVotingContractAddress: string | undefined; let votesTokenContractAddress: string | undefined; let underlyingTokenAddress: string | undefined; let lockReleaseContractAddress: string | undefined; - const masterCopyData = await getZodiacModuleProxyMasterCopyData( - getAddress(votingStrategyAddress), - ); - const isOzLinearVoting = masterCopyData.isOzLinearVoting; - const isOzLinearVotingERC721 = masterCopyData.isOzLinearVotingERC721; + const { isOzLinearVoting, isOzLinearVotingERC721 } = + await getZodiacModuleProxyMasterCopyData(votingStrategyAddress); if (isOzLinearVoting) { ozLinearVotingContractAddress = votingStrategyAddress; const ozLinearVotingContract = getContract({ abi: LinearERC20VotingAbi, - address: getAddress(ozLinearVotingContractAddress), + address: ozLinearVotingContractAddress, client: publicClient, }); const govTokenAddress = await ozLinearVotingContract.read.governanceToken(); const possibleERC20Wrapper = getContract({ abi: VotesERC20WrapperAbi, - address: getAddress(govTokenAddress), + address: govTokenAddress, client: publicClient, }); @@ -71,7 +70,7 @@ export const useGovernanceContracts = () => { return undefined; }); const possibleLockRelease = getContract({ - address: getAddress(govTokenAddress), + address: govTokenAddress, abi: LockReleaseAbi, client: { public: publicClient }, }); @@ -115,9 +114,9 @@ export const useGovernanceContracts = () => { action, baseContracts, fractalModules, + getVotingStrategyAddress, getZodiacModuleProxyMasterCopyData, publicClient, - votingStrategyAddress, ]); useEffect(() => { diff --git a/src/hooks/DAO/proposal/useSubmitProposal.ts b/src/hooks/DAO/proposal/useSubmitProposal.ts index d69c569173..9e8eb359a5 100644 --- a/src/hooks/DAO/proposal/useSubmitProposal.ts +++ b/src/hooks/DAO/proposal/useSubmitProposal.ts @@ -49,7 +49,7 @@ export default function useSubmitProposal() { const { data: walletClient } = useWalletClient(); const publicClient = usePublicClient(); - const votingContractAddress = useVotingStrategyAddress(); + const { getVotingStrategyAddress } = useVotingStrategyAddress(); const { node: { safe, fractalModules }, @@ -278,10 +278,11 @@ export default function useSubmitProposal() { if (safeAddress && isAddress(safeAddress)) { // Submitting proposal to any DAO out of global context + const votingStrategyAddress = await getVotingStrategyAddress(getAddress(safeAddress)); const safeInfo = await safeAPI.getSafeInfo(getAddress(safeAddress)); const modules = await lookupModules(safeInfo.modules); const azoriusModule = getAzoriusModuleFromModules(modules); - if (!azoriusModule) { + if (!azoriusModule || !votingStrategyAddress) { await submitMultisigProposal({ proposalData, pendingToastMessage, @@ -291,7 +292,7 @@ export default function useSubmitProposal() { successCallback, safeAddress, }); - } else if (walletClient && votingContractAddress) { + } else { await submitAzoriusProposal({ proposalData, pendingToastMessage, @@ -301,7 +302,7 @@ export default function useSubmitProposal() { successCallback, safeAddress, azoriusAddress: getAddress(azoriusModule.moduleAddress), - votingStrategyAddress: votingContractAddress, + votingStrategyAddress, }); } } else { @@ -338,6 +339,7 @@ export default function useSubmitProposal() { [ erc721LinearVotingContractAddress, freezeVotingContractAddress, + getVotingStrategyAddress, globalAzoriusContract, lookupModules, ozLinearVotingContractAddress, @@ -345,8 +347,6 @@ export default function useSubmitProposal() { safeAPI, submitAzoriusProposal, submitMultisigProposal, - votingContractAddress, - walletClient, ], ); diff --git a/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts b/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts index b0e1991e91..6138c34e8d 100644 --- a/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts +++ b/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts @@ -40,7 +40,7 @@ export default function useUserERC721VotingTokens( const safeAPI = useSafeAPI(); const publicClient = usePublicClient(); - const votingContractAddress = useVotingStrategyAddress(); + const { getVotingStrategyAddress } = useVotingStrategyAddress(); const azoriusGovernance = governance as AzoriusGovernance; const { erc721Tokens } = azoriusGovernance; @@ -69,11 +69,11 @@ export default function useUserERC721VotingTokens( if (_safeAddress && daoAddress !== _safeAddress) { // Means getting these for any safe, primary use case - calculating user voting weight for freeze voting - - if (votingContractAddress) { + const votingStrategyAddress = await getVotingStrategyAddress(getAddress(_safeAddress)); + if (votingStrategyAddress) { votingContract = getContract({ abi: LinearERC721VotingAbi, - address: getAddress(votingContractAddress), + address: votingStrategyAddress, client: publicClient, }); const addresses = await votingContract.read.getAllTokenAddresses(); @@ -204,11 +204,11 @@ export default function useUserERC721VotingTokens( daoAddress, erc721LinearVotingContractAddress, erc721Tokens, + getVotingStrategyAddress, publicClient, safeAPI, signerOrProvider, user.address, - votingContractAddress, ], ); diff --git a/src/hooks/utils/useCanUserSubmitProposal.ts b/src/hooks/utils/useCanUserSubmitProposal.ts index 263461d839..00e90f3757 100644 --- a/src/hooks/utils/useCanUserSubmitProposal.ts +++ b/src/hooks/utils/useCanUserSubmitProposal.ts @@ -19,7 +19,7 @@ export function useCanUserCreateProposal() { const [canUserCreateProposal, setCanUserCreateProposal] = useState(); const publicClient = usePublicClient(); - const votingContractAddress = useVotingStrategyAddress(); + const { getVotingStrategyAddress } = useVotingStrategyAddress(); /** * Performs a check whether user has access rights to create proposal for DAO @@ -38,10 +38,11 @@ export function useCanUserCreateProposal() { }; if (safeAddress) { - if (votingContractAddress) { + const votingStrategyAddress = await getVotingStrategyAddress(getAddress(safeAddress)); + if (votingStrategyAddress) { const votingContract = getContract({ abi: LinearERC20VotingAbi, - address: getAddress(votingContractAddress), + address: votingStrategyAddress, client: publicClient, }); const isProposer = await votingContract.read.isProposer([getAddress(user.address)]); @@ -78,13 +79,13 @@ export function useCanUserCreateProposal() { }, [ erc721LinearVotingContractAddress, + getVotingStrategyAddress, ozLinearVotingContractAddress, publicClient, safe, safeAPI, type, user.address, - votingContractAddress, ], ); useEffect(() => { diff --git a/src/hooks/utils/useVotingStrategyAddress.ts b/src/hooks/utils/useVotingStrategyAddress.ts index ee82844b9f..f228a42113 100644 --- a/src/hooks/utils/useVotingStrategyAddress.ts +++ b/src/hooks/utils/useVotingStrategyAddress.ts @@ -1,22 +1,40 @@ -import { useEffect, useState } from 'react'; import { Address, getAddress, getContract } from 'viem'; import { usePublicClient } from 'wagmi'; import AzoriusAbi from '../../assets/abi/Azorius'; import { SENTINEL_ADDRESS } from '../../constants/common'; import { useFractal } from '../../providers/App/AppProvider'; +import { useSafeAPI } from '../../providers/App/hooks/useSafeAPI'; +import { FractalModuleData } from '../../types'; import { getAzoriusModuleFromModules } from '../../utils'; +import { useFractalModules } from '../DAO/loaders/useFractalModules'; const useVotingStrategyAddress = () => { - const [votingStrategyAddress, setVotingStrategyAddress] = useState
(); - const { node } = useFractal(); const publicClient = usePublicClient(); + const safeAPI = useSafeAPI(); + const lookupModules = useFractalModules(); + + const getVotingStrategyAddress = async (safeAddress?: Address) => { + let azoriusModule: FractalModuleData | undefined; + + if (safeAddress) { + if (!safeAPI) { + throw new Error('Safe API not ready'); + } + const safeInfo = await safeAPI.getSafeInfo(getAddress(safeAddress)); + const safeModules = await lookupModules(safeInfo.modules); + azoriusModule = getAzoriusModuleFromModules(safeModules); + if (!azoriusModule) return; + } else { + azoriusModule = getAzoriusModuleFromModules(node.fractalModules); + } - useEffect(() => { - const azoriusModule = getAzoriusModuleFromModules(node.fractalModules); + if (!azoriusModule) { + throw new Error('Azorius module not found'); + } - if (!azoriusModule || !publicClient) { - return; + if (!publicClient) { + throw new Error('Public client undefined'); } const azoriusContract = getContract({ @@ -26,12 +44,11 @@ const useVotingStrategyAddress = () => { }); // @dev assumes the first strategy is the voting contract - azoriusContract.read.getStrategies([SENTINEL_ADDRESS, 0n]).then(strategies => { - setVotingStrategyAddress(strategies[1]); - }); - }, [node.fractalModules, publicClient]); + const strategies = await azoriusContract.read.getStrategies([SENTINEL_ADDRESS, 0n]); + return strategies[1]; + }; - return votingStrategyAddress; + return { getVotingStrategyAddress }; }; export default useVotingStrategyAddress;