From dc067c81f85683a7cdad97052ad65cfc33b72279 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Mar 2024 02:33:45 -0400 Subject: [PATCH 01/30] remove freeze guard and freeze voting from state --- .../MultisigProposalDetails/TxActions.tsx | 18 ++-- .../MultisigProposalDetails/index.tsx | 9 +- src/components/Proposals/index.tsx | 4 +- .../DaoDashboard/Info/InfoGovernance.tsx | 17 ++-- .../DaoSettings/components/Modules/index.tsx | 6 +- .../ui/menus/ManageDAO/ManageDAOMenu.tsx | 24 ++--- .../ui/proposal/useProposalCountdown.tsx | 15 ++- src/hooks/DAO/loaders/useFractalFreeze.ts | 96 +++++++++++-------- src/hooks/DAO/loaders/useFractalGovernance.ts | 4 +- src/hooks/DAO/proposal/useSubmitProposal.ts | 6 +- src/hooks/DAO/useCastFreezeVote.ts | 28 +++--- src/hooks/utils/useSafeTransactions.ts | 12 ++- src/providers/App/guardContracts/reducer.ts | 4 +- src/types/fractal.ts | 13 +-- 14 files changed, 137 insertions(+), 119 deletions(-) diff --git a/src/components/Proposals/MultisigProposalDetails/TxActions.tsx b/src/components/Proposals/MultisigProposalDetails/TxActions.tsx index 9a3b96255e..30cd4ec77a 100644 --- a/src/components/Proposals/MultisigProposalDetails/TxActions.tsx +++ b/src/components/Proposals/MultisigProposalDetails/TxActions.tsx @@ -1,7 +1,6 @@ import { Box, Button, Text, Flex } from '@chakra-ui/react'; import { Check } from '@decent-org/fractal-ui'; import { TypedDataSigner } from '@ethersproject/abstract-signer'; -import { MultisigFreezeGuard } from '@fractal-framework/fractal-contracts'; import { SafeMultisigTransactionWithTransfersResponse } from '@safe-global/safe-service-client'; import { Signer } from 'ethers'; import { useTranslation } from 'react-i18next'; @@ -10,6 +9,7 @@ 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 useSafeContracts from '../../../hooks/safe/useSafeContracts'; import { useAsyncRequest } from '../../../hooks/utils/useAsyncRequest'; import useSignerOrProvider from '../../../hooks/utils/useSignerOrProvider'; import { useTransaction } from '../../../hooks/utils/useTransaction'; @@ -20,15 +20,10 @@ import { MultisigProposal, FractalProposalState } from '../../../types'; import ContentBox from '../../ui/containers/ContentBox'; import { ProposalCountdown } from '../../ui/proposal/ProposalCountdown'; -export function TxActions({ - proposal, - freezeGuard, -}: { - proposal: MultisigProposal; - freezeGuard: MultisigFreezeGuard; -}) { +export function TxActions({ proposal }: { proposal: MultisigProposal }) { const { node: { safe }, + guardContracts: { freezeGuardContractAddress }, readOnly: { user }, } = useFractal(); const signerOrProvider = useSignerOrProvider(); @@ -40,7 +35,7 @@ export function TxActions({ const [asyncRequest, asyncRequestPending] = useAsyncRequest(); const [contractCall, contractCallPending] = useTransaction(); const loadSafeMultisigProposals = useSafeMultisigProposals(); - + const baseContracts = useSafeContracts(); if (user.votingWeight.eq(0)) return <>; const multisigTx = proposal.transaction as SafeMultisigTransactionWithTransfersResponse; @@ -78,7 +73,7 @@ export function TxActions({ const timelockTransaction = async () => { try { - if (!multisigTx.confirmations) { + if (!multisigTx.confirmations || !baseContracts || !freezeGuardContractAddress) { return; } const safeTx = buildSafeTransaction({ @@ -90,6 +85,9 @@ export function TxActions({ data: confirmation.signature, })), ); + const freezeGuard = baseContracts.multisigFreezeGuardMasterCopyContract.asSigner.attach( + freezeGuardContractAddress, + ); contractCall({ contractFn: () => freezeGuard.timelockTransaction( diff --git a/src/components/Proposals/MultisigProposalDetails/index.tsx b/src/components/Proposals/MultisigProposalDetails/index.tsx index 5b2b1a37df..29a02ac73b 100644 --- a/src/components/Proposals/MultisigProposalDetails/index.tsx +++ b/src/components/Proposals/MultisigProposalDetails/index.tsx @@ -1,5 +1,4 @@ import { GridItem, Box } from '@chakra-ui/react'; -import { MultisigFreezeGuard } from '@fractal-framework/fractal-contracts'; import { BACKGROUND_SEMI_TRANSPARENT } from '../../../constants/common'; import { useFractal } from '../../../providers/App/AppProvider'; import { FractalProposal, MultisigProposal } from '../../../types'; @@ -14,7 +13,6 @@ import { TxDetails } from './TxDetails'; export function MultisigProposalDetails({ proposal }: { proposal: FractalProposal }) { const txProposal = proposal as MultisigProposal; const { - guardContracts: { freezeGuardContract }, readOnly: { user }, } = useFractal(); return ( @@ -30,12 +28,7 @@ export function MultisigProposalDetails({ proposal }: { proposal: FractalProposa - {user.address && ( - - )} + {user.address && } ); diff --git a/src/components/Proposals/index.tsx b/src/components/Proposals/index.tsx index bc99f9488d..f324ce185e 100644 --- a/src/components/Proposals/index.tsx +++ b/src/components/Proposals/index.tsx @@ -65,7 +65,7 @@ export default function Proposals() { break; case GovernanceType.MULTISIG: default: - if (guardContracts.freezeGuardContract) { + if (guardContracts.freezeGuardContractAddress) { options = FILTERS_MULTISIG_CHILD; } else { options = FILTERS_MULTISIG_BASE; @@ -77,7 +77,7 @@ export default function Proposals() { } setAllOptions(options); setFilters(options); - }, [daoSnapshotURL, guardContracts.freezeGuardContract, type]); + }, [daoSnapshotURL, guardContracts.freezeGuardContractAddress, type]); const toggleFilter = (filter: FractalProposalState) => { setFilters(prevState => { diff --git a/src/components/pages/DaoDashboard/Info/InfoGovernance.tsx b/src/components/pages/DaoDashboard/Info/InfoGovernance.tsx index 611b02fbad..20bb18f4c9 100644 --- a/src/components/pages/DaoDashboard/Info/InfoGovernance.tsx +++ b/src/components/pages/DaoDashboard/Info/InfoGovernance.tsx @@ -1,8 +1,8 @@ import { Box, Flex, Text } from '@chakra-ui/react'; import { Govern } from '@decent-org/fractal-ui'; -import { MultisigFreezeGuard } from '@fractal-framework/fractal-contracts'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import useSafeContracts from '../../../../hooks/safe/useSafeContracts'; import { useTimeHelpers } from '../../../../hooks/utils/useTimeHelpers'; import { useFractal } from '../../../../providers/App/AppProvider'; import { useEthersProvider } from '../../../../providers/Ethers/hooks/useEthersProvider'; @@ -15,11 +15,12 @@ export function InfoGovernance() { const { node: { daoAddress }, governance, - guardContracts: { freezeGuardType, freezeGuardContract }, + guardContracts: { freezeGuardType, freezeGuardContractAddress }, readOnly: { dao }, } = useFractal(); const provider = useEthersProvider(); const { getTimeDuration } = useTimeHelpers(); + const baseContracts = useSafeContracts(); const [timelockPeriod, setTimelockPeriod] = useState(); const [executionPeriod, setExecutionPeriod] = useState(); useEffect(() => { @@ -30,10 +31,11 @@ export function InfoGovernance() { } }; if (freezeGuardType == FreezeGuardType.MULTISIG) { - if (freezeGuardContract) { - const freezeGuard = freezeGuardContract.asProvider as MultisigFreezeGuard; - setTimelockPeriod(await formatBlocks(await freezeGuard.timelockPeriod())); - setExecutionPeriod(await formatBlocks(await freezeGuard.executionPeriod())); + if (freezeGuardContractAddress && baseContracts) { + const freezeGuardContract = + baseContracts.multisigFreezeGuardMasterCopyContract.asProvider; + setTimelockPeriod(await formatBlocks(await freezeGuardContract.timelockPeriod())); + setExecutionPeriod(await formatBlocks(await freezeGuardContract.executionPeriod())); } } else if (dao?.isAzorius) { const azoriusGovernance = governance as AzoriusGovernance; @@ -54,10 +56,11 @@ export function InfoGovernance() { setTimelockInfo(); }, [ + baseContracts, executionPeriod, getTimeDuration, governance, - freezeGuardContract, + freezeGuardContractAddress, freezeGuardType, provider, timelockPeriod, diff --git a/src/components/pages/DaoSettings/components/Modules/index.tsx b/src/components/pages/DaoSettings/components/Modules/index.tsx index 1f5cd5c3a3..0b6479813b 100644 --- a/src/components/pages/DaoSettings/components/Modules/index.tsx +++ b/src/components/pages/DaoSettings/components/Modules/index.tsx @@ -26,7 +26,7 @@ export function ModulesContainer() { const { t } = useTranslation(['settings']); const { node: { fractalModules, isModulesLoaded, safe }, - guardContracts: { freezeGuardContract, freezeVotingContract }, + guardContracts: { freezeGuardContractAddress, freezeVotingContractAddress }, } = useFractal(); return ( @@ -91,7 +91,9 @@ export function ModulesContainer() { {safe.guard} - {!!freezeGuardContract || !!freezeVotingContract ? ' (Freeze Guard)' : ''} + {!!freezeGuardContractAddress || !!freezeVotingContractAddress + ? ' (Freeze Guard)' + : ''} ) : ( diff --git a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx index 915ccbec71..70d3217520 100644 --- a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx +++ b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx @@ -1,9 +1,5 @@ import { VEllipsis } from '@decent-org/fractal-ui'; -import { - ERC20FreezeVoting, - ERC721FreezeVoting, - MultisigFreezeVoting, -} from '@fractal-framework/fractal-contracts'; +import { ERC20FreezeVoting, MultisigFreezeVoting } from '@fractal-framework/fractal-contracts'; import { BigNumber } from 'ethers'; import { useMemo, useCallback, useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; @@ -15,6 +11,7 @@ import { import useSubmitProposal from '../../../../hooks/DAO/proposal/useSubmitProposal'; import useUserERC721VotingTokens from '../../../../hooks/DAO/proposal/useUserERC721VotingTokens'; import useClawBack from '../../../../hooks/DAO/useClawBack'; +import useSafeContracts from '../../../../hooks/safe/useSafeContracts'; import useBlockTimestamp from '../../../../hooks/utils/useBlockTimestamp'; import { useMasterCopy } from '../../../../hooks/utils/useMasterCopy'; import { useFractal } from '../../../../providers/App/AppProvider'; @@ -58,8 +55,8 @@ export function ManageDAOMenu({ const { node: { safe }, governance: { type }, - baseContracts, } = useFractal(); + const baseContracts = useSafeContracts(); const currentTime = BigNumber.from(useBlockTimestamp()); const navigate = useNavigate(); const safeAddress = fractalNode?.daoAddress; @@ -142,7 +139,7 @@ export function ManageDAOMenu({ const freezeOption = { optionKey: 'optionInitiateFreeze', onClick: () => { - const freezeVotingContract = guardContracts?.freezeVotingContract?.asSigner; + const freezeVotingContract = baseContracts?.freezeMultisigVotingMasterCopyContract.asSigner; const freezeVotingType = guardContracts?.freezeVotingType; if (freezeVotingContract) { if ( @@ -152,9 +149,12 @@ export function ManageDAOMenu({ (freezeVotingContract as ERC20FreezeVoting | MultisigFreezeVoting).castFreezeVote(); } else if (freezeVotingType === FreezeVotingType.ERC721) { getUserERC721VotingTokens(undefined, parentAddress).then(tokensInfo => { - return (freezeVotingContract as ERC721FreezeVoting)[ - 'castFreezeVote(address[],uint256[])' - ](tokensInfo.totalVotingTokenAddresses, tokensInfo.totalVotingTokenIds); + const freezeERC721VotingContract = + baseContracts?.freezeERC721VotingMasterCopyContract.asSigner; + return freezeERC721VotingContract['castFreezeVote(address[],uint256[])']( + tokensInfo.totalVotingTokenAddresses, + tokensInfo.totalVotingTokenIds, + ); }); } } @@ -229,13 +229,13 @@ export function ManageDAOMenu({ safeAddress, parentAddress, governanceType, - guardContracts?.freezeVotingContract?.asSigner, - guardContracts?.freezeVotingType, + guardContracts, handleClawBack, canUserCreateProposal, handleModifyGovernance, handleNavigateToSettings, getUserERC721VotingTokens, + baseContracts, ]); return ( diff --git a/src/components/ui/proposal/useProposalCountdown.tsx b/src/components/ui/proposal/useProposalCountdown.tsx index a0d46de093..8c5e153ed1 100644 --- a/src/components/ui/proposal/useProposalCountdown.tsx +++ b/src/components/ui/proposal/useProposalCountdown.tsx @@ -5,6 +5,7 @@ import { logError } from '../../../helpers/errorLogging'; import useSnapshotProposal from '../../../hooks/DAO/loaders/snapshot/useSnapshotProposal'; import { useDAOProposals } from '../../../hooks/DAO/loaders/useProposals'; import useUpdateProposalState from '../../../hooks/DAO/proposal/useUpdateProposalState'; +import useSafeContracts from '../../../hooks/safe/useSafeContracts'; import { useFractal } from '../../../providers/App/AppProvider'; import { useEthersProvider } from '../../../providers/Ethers/hooks/useEthersProvider'; import { @@ -20,11 +21,12 @@ import { getTxTimelockedTimestamp } from '../../../utils/guard'; export function useProposalCountdown(proposal: FractalProposal) { const { governance, - guardContracts: { freezeGuardContract, freezeGuardType }, + guardContracts: { freezeGuardContractAddress, freezeGuardType }, governanceContracts, action, readOnly: { dao }, } = useFractal(); + const baseContracts = useSafeContracts(); const provider = useEthersProvider(); const [secondsLeft, setSecondsLeft] = useState(); @@ -97,9 +99,9 @@ export function useProposalCountdown(proposal: FractalProposal) { async function getCountdown() { const freezeGuard = freezeGuardType === FreezeGuardType.MULTISIG - ? (freezeGuardContract?.asProvider as MultisigFreezeGuard) + ? baseContracts?.multisigFreezeGuardMasterCopyContract.asProvider : freezeGuardType === FreezeGuardType.AZORIUS - ? (freezeGuardContract?.asProvider as AzoriusFreezeGuard) + ? (baseContracts?.azoriusFreezeGuardMasterCopyContract.asProvider as AzoriusFreezeGuard) : undefined; const isSafeGuard = freezeGuardType === FreezeGuardType.MULTISIG; @@ -164,7 +166,12 @@ export function useProposalCountdown(proposal: FractalProposal) { clearInterval(countdownInterval); }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [proposal.state, azoriusGovernance.votingStrategy, freezeGuardContract, freezeGuardType]); + }, [ + proposal.state, + azoriusGovernance.votingStrategy, + freezeGuardContractAddress, + freezeGuardType, + ]); return secondsLeft; } diff --git a/src/hooks/DAO/loaders/useFractalFreeze.ts b/src/hooks/DAO/loaders/useFractalFreeze.ts index 3309a382c5..a24fea1a78 100644 --- a/src/hooks/DAO/loaders/useFractalFreeze.ts +++ b/src/hooks/DAO/loaders/useFractalFreeze.ts @@ -8,7 +8,6 @@ import { FreezeVoteCastEvent } from '@fractal-framework/fractal-contracts/dist/t import { BigNumber, constants } from 'ethers'; import { useCallback, useEffect, useRef } from 'react'; import { useAccount } from 'wagmi'; -import { getEventRPC } from '../../../helpers'; import { isWithinFreezeProposalPeriod, isWithinFreezePeriod, @@ -16,8 +15,9 @@ import { import { useFractal } from '../../../providers/App/AppProvider'; import { FractalGuardAction } from '../../../providers/App/guard/action'; import { useEthersProvider } from '../../../providers/Ethers/hooks/useEthersProvider'; -import { ContractConnection, FractalGuardContracts, FreezeVotingType } from '../../../types'; +import { FractalGuardContracts, FreezeVotingType } from '../../../types'; import { blocksToSeconds, getTimeStamp } from '../../../utils/contract'; +import useSafeContracts from '../../safe/useSafeContracts'; import useUserERC721VotingTokens from '../proposal/useUserERC721VotingTokens'; import { FreezeGuard } from './../../../types/fractal'; @@ -34,10 +34,10 @@ export const useFractalFreeze = ({ const { node: { daoAddress }, - baseContracts, guardContracts, action, } = useFractal(); + const baseContracts = useSafeContracts(); const { address: account } = useAccount(); const { getUserERC721VotingTokens } = useUserERC721VotingTokens( undefined, @@ -48,41 +48,44 @@ export const useFractalFreeze = ({ const provider = useEthersProvider(); const loadFractalFreezeGuard = useCallback( - async ({ freezeVotingContract, freezeVotingType: freezeVotingType }: FractalGuardContracts) => { + async ({ + freezeVotingContractAddress, + freezeVotingType: freezeVotingType, + }: FractalGuardContracts) => { if ( freezeVotingType == null || - !freezeVotingContract || + !freezeVotingContractAddress || !account || !provider || !baseContracts - ) + ) { return; + } + + // @dev using freeze 'multisig' contract but these functions are the same for all freeze types + const freezeVotingContract = + baseContracts.freezeMultisigVotingMasterCopyContract.asSigner.attach( + freezeVotingContractAddress, + ); let userHasVotes: boolean = false; - const freezeCreatedBlock = await ( - freezeVotingContract!.asProvider as - | ERC20FreezeVoting - | ERC721FreezeVoting - | MultisigFreezeVoting - ).freezeProposalCreatedBlock(); - - const freezeVotesThreshold = await freezeVotingContract!.asProvider.freezeVotesThreshold(); - const freezeProposalCreatedBlock = - await freezeVotingContract!.asProvider.freezeProposalCreatedBlock(); + const freezeCreatedBlock = await freezeVotingContract.freezeProposalCreatedBlock(); + + const freezeVotesThreshold = await freezeVotingContract.freezeVotesThreshold(); + const freezeProposalCreatedBlock = await freezeVotingContract.freezeProposalCreatedBlock(); const freezeProposalCreatedTime = await getTimeStamp(freezeProposalCreatedBlock, provider); - const freezeProposalVoteCount = - await freezeVotingContract!.asProvider.freezeProposalVoteCount(); - const freezeProposalBlock = await freezeVotingContract!.asProvider.freezeProposalPeriod(); + const freezeProposalVoteCount = await freezeVotingContract.freezeProposalVoteCount(); + const freezeProposalBlock = await freezeVotingContract.freezeProposalPeriod(); // length of time to vote on freeze const freezeProposalPeriod = await blocksToSeconds(freezeProposalBlock, provider); - const freezePeriodBlock = await freezeVotingContract!.asProvider.freezePeriod(); + const freezePeriodBlock = await freezeVotingContract.freezePeriod(); // length of time frozen for in seconds const freezePeriod = await blocksToSeconds(freezePeriodBlock, provider); - const userHasFreezeVoted = await freezeVotingContract!.asProvider.userHasFreezeVoted( + const userHasFreezeVoted = await freezeVotingContract.userHasFreezeVoted( account || constants.AddressZero, freezeCreatedBlock, ); - const isFrozen = await freezeVotingContract!.asProvider.isFrozen(); + const isFrozen = await freezeVotingContract.isFrozen(); const freezeGuard = { freezeVotesThreshold, @@ -94,17 +97,24 @@ export const useFractalFreeze = ({ isFrozen, }; - const { votesTokenMasterCopyContract, safeSingletonContract } = baseContracts; + const { + votesTokenMasterCopyContract, + safeSingletonContract, + freezeERC20VotingMasterCopyContract, + } = baseContracts; if (freezeVotingType === FreezeVotingType.MULTISIG) { const safeContract = safeSingletonContract!.asProvider.attach( - await (freezeVotingContract!.asProvider as MultisigFreezeVoting).parentGnosisSafe(), + await (freezeVotingContract as MultisigFreezeVoting).parentGnosisSafe(), ); const owners = await safeContract.getOwners(); userHasVotes = owners.find(owner => owner === account) !== undefined; } else if (freezeVotingType === FreezeVotingType.ERC20) { + const freezeERC20VotingContract = freezeERC20VotingMasterCopyContract.asSigner.attach( + freezeVotingContractAddress, + ); const votesTokenContract = votesTokenMasterCopyContract!.asProvider.attach( - await (freezeVotingContract!.asProvider as ERC20FreezeVoting).votesERC20(), + await freezeERC20VotingContract.votesERC20(), ); const currentTimestamp = await getTimeStamp('latest', provider); const isFreezeActive = @@ -156,11 +166,11 @@ export const useFractalFreeze = ({ useEffect(() => { if ( guardContracts.freezeVotingType !== null && - !!guardContracts.freezeVotingContract && + !!guardContracts.freezeVotingContractAddress && loadOnMount && - guardContracts.freezeVotingContract.asProvider.address !== loadKey.current + guardContracts.freezeVotingContractAddress !== loadKey.current ) { - loadKey.current = guardContracts.freezeVotingContract.asProvider.address; + loadKey.current = guardContracts.freezeVotingContractAddress; setFractalFreezeGuard(guardContracts); } if (!daoAddress) { @@ -169,9 +179,18 @@ export const useFractalFreeze = ({ }, [setFractalFreezeGuard, guardContracts, daoAddress, loadOnMount]); useEffect(() => { - if (!loadOnMount || !provider) return; - const { freezeVotingContract, freezeVotingType: freezeVotingType } = guardContracts; - let votingRPC: MultisigFreezeVoting | ERC20FreezeVoting | ERC721FreezeVoting; + const { freezeVotingContractAddress, freezeVotingType: freezeVotingType } = guardContracts; + if (!loadOnMount || !provider || !baseContracts || !freezeVotingContractAddress) return; + const { + freezeERC721VotingMasterCopyContract, + freezeMultisigVotingMasterCopyContract, + freezeERC20VotingMasterCopyContract, + } = baseContracts; + + // @dev using freeze 'multisig' contract but these functions are the same for all freeze types + let votingRPC: MultisigFreezeVoting | ERC20FreezeVoting | ERC721FreezeVoting = + freezeMultisigVotingMasterCopyContract.asSigner.attach(freezeVotingContractAddress); + const listenerCallback: TypedListener = async ( voter: string, votesCast, @@ -189,27 +208,24 @@ export const useFractalFreeze = ({ }); }; - if (isFreezeSet.current && freezeVotingType !== null && freezeVotingContract) { + if (isFreezeSet.current && freezeVotingType !== null && freezeVotingContractAddress) { if (freezeVotingType === FreezeVotingType.MULTISIG) { - votingRPC = getEventRPC( - freezeVotingContract as ContractConnection, - ); const filter = votingRPC.filters.FreezeVoteCast(); votingRPC.on(filter, listenerCallback); } else if (freezeVotingType === FreezeVotingType.ERC20) { - votingRPC = getEventRPC( - freezeVotingContract as ContractConnection, + votingRPC = freezeERC20VotingMasterCopyContract.asProvider.attach( + freezeVotingContractAddress, ); const filter = votingRPC.filters.FreezeVoteCast(); votingRPC.on(filter, listenerCallback); } else if (freezeVotingType === FreezeVotingType.ERC721) { - votingRPC = getEventRPC( - freezeVotingContract as ContractConnection, + votingRPC = freezeERC721VotingMasterCopyContract.asProvider.attach( + freezeVotingContractAddress, ); const filter = votingRPC.filters.FreezeVoteCast(); votingRPC.on(filter, listenerCallback); } } - }, [guardContracts, account, action, loadOnMount, provider]); + }, [guardContracts, account, action, loadOnMount, provider, baseContracts]); return loadFractalFreezeGuard; }; diff --git a/src/hooks/DAO/loaders/useFractalGovernance.ts b/src/hooks/DAO/loaders/useFractalGovernance.ts index b17d4a586c..9355b5f881 100644 --- a/src/hooks/DAO/loaders/useFractalGovernance.ts +++ b/src/hooks/DAO/loaders/useFractalGovernance.ts @@ -83,7 +83,7 @@ export const useFractalGovernance = () => { const newLoadKey = (azoriusContractAddress ? '1' : '0') + nodeHierarchy.parentAddress + - !!guardContracts.freezeGuardContract; + !!guardContracts.freezeGuardContractAddress; if (isLoaded && newLoadKey !== loadKey.current) { loadKey.current = newLoadKey; @@ -126,7 +126,7 @@ export const useFractalGovernance = () => { loadERC20Token, loadLockedVotesToken, nodeHierarchy.parentAddress, - guardContracts.freezeGuardContract, + guardContracts.freezeGuardContractAddress, loadERC721Strategy, loadERC721Tokens, action, diff --git a/src/hooks/DAO/proposal/useSubmitProposal.ts b/src/hooks/DAO/proposal/useSubmitProposal.ts index 49be918ada..a382abf759 100644 --- a/src/hooks/DAO/proposal/useSubmitProposal.ts +++ b/src/hooks/DAO/proposal/useSubmitProposal.ts @@ -65,7 +65,7 @@ export default function useSubmitProposal() { const { node: { safe, fractalModules }, - guardContracts: { freezeVotingContract }, + guardContracts: { freezeVotingContractAddress }, governanceContracts: { ozLinearVotingContractAddress, erc721LinearVotingContractAddress }, governance: { type }, readOnly: { user }, @@ -407,7 +407,7 @@ export default function useSubmitProposal() { const votingStrategyAddress = ozLinearVotingContractAddress || erc721LinearVotingContractAddress || - freezeVotingContract?.asProvider.address; + freezeVotingContractAddress; if (!globalAzoriusContract || !votingStrategyAddress) { await submitMultisigProposal({ @@ -436,7 +436,7 @@ export default function useSubmitProposal() { }, [ globalAzoriusContract, - freezeVotingContract, + freezeVotingContractAddress, safe, lookupModules, submitMultisigProposal, diff --git a/src/hooks/DAO/useCastFreezeVote.ts b/src/hooks/DAO/useCastFreezeVote.ts index ae5513949a..937e31e7c3 100644 --- a/src/hooks/DAO/useCastFreezeVote.ts +++ b/src/hooks/DAO/useCastFreezeVote.ts @@ -1,12 +1,8 @@ -import { - ERC20FreezeVoting, - ERC721FreezeVoting, - MultisigFreezeVoting, -} from '@fractal-framework/fractal-contracts'; import { useCallback, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useFractal } from '../../providers/App/AppProvider'; import { FreezeVotingType } from '../../types'; +import useSafeContracts from '../safe/useSafeContracts'; import { useTransaction } from '../utils/useTransaction'; import useUserERC721VotingTokens from './proposal/useUserERC721VotingTokens'; @@ -17,11 +13,12 @@ const useCastFreezeVote = ({ }) => { const [contractCallCastFreeze, contractCallPending] = useTransaction(); const { - guardContracts: { freezeVotingContract, freezeVotingType }, + guardContracts: { freezeVotingContractAddress, freezeVotingType }, node: { nodeHierarchy: { parentAddress }, }, } = useFractal(); + const baseContracts = useSafeContracts(); const { getUserERC721VotingTokens } = useUserERC721VotingTokens(undefined, undefined, false); useEffect(() => { @@ -31,18 +28,22 @@ const useCastFreezeVote = ({ const { t } = useTranslation('transaction'); const castFreezeVote = useCallback(() => { + if (!freezeVotingContractAddress || !baseContracts) return; contractCallCastFreeze({ contractFn: () => { if (freezeVotingType === FreezeVotingType.ERC721) { + const freezeERC721VotingContract = + baseContracts.freezeERC721VotingMasterCopyContract.asSigner; return getUserERC721VotingTokens(undefined, parentAddress).then(tokensInfo => - (freezeVotingContract?.asSigner as ERC721FreezeVoting)[ - 'castFreezeVote(address[],uint256[])' - ](tokensInfo.totalVotingTokenAddresses, tokensInfo.totalVotingTokenIds), + freezeERC721VotingContract['castFreezeVote(address[],uint256[])']( + tokensInfo.totalVotingTokenAddresses, + tokensInfo.totalVotingTokenIds, + ), ); } else { - return ( - freezeVotingContract?.asSigner as MultisigFreezeVoting | ERC20FreezeVoting - ).castFreezeVote(); + const freezeVotingContract = + baseContracts.freezeMultisigVotingMasterCopyContract.asSigner; + return freezeVotingContract.castFreezeVote(); } }, pendingMessage: t('pendingCastFreezeVote'), @@ -52,10 +53,11 @@ const useCastFreezeVote = ({ }, [ contractCallCastFreeze, t, - freezeVotingContract, + freezeVotingContractAddress, freezeVotingType, getUserERC721VotingTokens, parentAddress, + baseContracts, ]); return castFreezeVote; }; diff --git a/src/hooks/utils/useSafeTransactions.ts b/src/hooks/utils/useSafeTransactions.ts index 1caaaf525c..e9c46c08ed 100644 --- a/src/hooks/utils/useSafeTransactions.ts +++ b/src/hooks/utils/useSafeTransactions.ts @@ -21,6 +21,7 @@ import { import { formatWeiToValue, isModuleTx, isMultiSigTx, parseDecodedData } from '../../utils'; import { getAverageBlockTime } from '../../utils/contract'; import { getTxTimelockedTimestamp } from '../../utils/guard'; +import useSafeContracts from '../safe/useSafeContracts'; import { useSafeDecoder } from './useSafeDecoder'; type FreezeGuardData = { @@ -33,6 +34,7 @@ export const useSafeTransactions = () => { const { nativeTokenSymbol } = useNetworkConfig(); const provider = useEthersProvider(); const { guardContracts } = useFractal(); + const baseContracts = useSafeContracts(); const decode = useSafeDecoder(); const getState = useCallback( @@ -318,10 +320,13 @@ export const useSafeTransactions = () => { let freezeGuard: MultisigFreezeGuard | undefined; let freezeGuardData: FreezeGuardData | undefined; - if (guardContracts.freezeGuardContract) { + if (guardContracts.freezeGuardContractAddress && baseContracts) { const blockNumber = await provider.getBlockNumber(); const averageBlockTime = await getAverageBlockTime(provider); - freezeGuard = guardContracts.freezeGuardContract.asProvider as MultisigFreezeGuard; + + freezeGuard = baseContracts.multisigFreezeGuardMasterCopyContract.asSigner.attach( + guardContracts.freezeGuardContractAddress, + ); freezeGuardData = { guardTimelockPeriodMs: BigNumber.from( (await freezeGuard.timelockPeriod()) * averageBlockTime * 1000, @@ -337,12 +342,13 @@ export const useSafeTransactions = () => { return activitiesWithState; }, [ - guardContracts.freezeGuardContract, + guardContracts, getState, getTransferTotal, decode, nativeTokenSymbol, provider, + baseContracts, ], ); return { parseTransactions }; diff --git a/src/providers/App/guardContracts/reducer.ts b/src/providers/App/guardContracts/reducer.ts index daf4ab768b..b2d528efee 100644 --- a/src/providers/App/guardContracts/reducer.ts +++ b/src/providers/App/guardContracts/reducer.ts @@ -4,8 +4,8 @@ import { GuardContractAction, GuardContractActions } from './action'; export const initialGuardContractsState: FractalGuardContracts = { freezeGuardType: null, freezeVotingType: null, - freezeGuardContract: undefined, - freezeVotingContract: undefined, + freezeGuardContractAddress: undefined, + freezeVotingContractAddress: undefined, }; export const guardContractReducer = ( diff --git a/src/types/fractal.ts b/src/types/fractal.ts index 9d8d5899fa..3bfc34debb 100644 --- a/src/types/fractal.ts +++ b/src/types/fractal.ts @@ -255,18 +255,9 @@ export enum FractalModuleType { FRACTAL, UNKNOWN, } -// @todo updates Fractal Guard Contract to just store addresses in the store -// export interface FractalGuardContracts { -// freezeGuardContractAddress?: string; -// freezeVotingContractAddress?: string; -// freezeGuardType: FreezeGuardType | null; -// freezeVotingType: FreezeVotingType | null; -// } export interface FractalGuardContracts { - freezeGuardContract?: ContractConnection; - freezeVotingContract?: ContractConnection< - ERC20FreezeVoting | ERC721FreezeVoting | MultisigFreezeVoting - >; + freezeGuardContractAddress?: string; + freezeVotingContractAddress?: string; freezeGuardType: FreezeGuardType | null; freezeVotingType: FreezeVotingType | null; } From cb68dda631d8b1edb6b373d25182239a70f0dfcc Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Mar 2024 02:34:07 -0400 Subject: [PATCH 02/30] update freeze and guard loader hooks --- src/hooks/DAO/loaders/useFractalFreeze.ts | 10 ++-- .../DAO/loaders/useFractalGuardContracts.ts | 51 ++++++++----------- 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/src/hooks/DAO/loaders/useFractalFreeze.ts b/src/hooks/DAO/loaders/useFractalFreeze.ts index a24fea1a78..9ef0cc7b3e 100644 --- a/src/hooks/DAO/loaders/useFractalFreeze.ts +++ b/src/hooks/DAO/loaders/useFractalFreeze.ts @@ -33,7 +33,6 @@ export const useFractalFreeze = ({ const isFreezeSet = useRef(false); const { - node: { daoAddress }, guardContracts, action, } = useFractal(); @@ -168,15 +167,16 @@ export const useFractalFreeze = ({ guardContracts.freezeVotingType !== null && !!guardContracts.freezeVotingContractAddress && loadOnMount && - guardContracts.freezeVotingContractAddress !== loadKey.current + guardContracts.freezeVotingContractAddress + parentSafeAddress !== loadKey.current ) { - loadKey.current = guardContracts.freezeVotingContractAddress; + console.count('load freeze guard') setFractalFreezeGuard(guardContracts); + loadKey.current = guardContracts.freezeVotingContractAddress + parentSafeAddress; } - if (!daoAddress) { + if (!parentSafeAddress) { loadKey.current = undefined; } - }, [setFractalFreezeGuard, guardContracts, daoAddress, loadOnMount]); + }, [setFractalFreezeGuard, guardContracts, parentSafeAddress, loadOnMount]); useEffect(() => { const { freezeVotingContractAddress, freezeVotingType: freezeVotingType } = guardContracts; diff --git a/src/hooks/DAO/loaders/useFractalGuardContracts.ts b/src/hooks/DAO/loaders/useFractalGuardContracts.ts index c848579476..716ccd919b 100644 --- a/src/hooks/DAO/loaders/useFractalGuardContracts.ts +++ b/src/hooks/DAO/loaders/useFractalGuardContracts.ts @@ -17,7 +17,14 @@ export const useFractalGuardContracts = ({ loadOnMount = true }: { loadOnMount?: // load key for component; helps prevent unnecessary calls const loadKey = useRef(); const { - node: { daoAddress, safe, fractalModules, isModulesLoaded }, + node: { + daoAddress, + safe, + fractalModules, + isModulesLoaded, + nodeHierarchy: { parentAddress }, + }, + baseContracts, action, } = useFractal(); @@ -35,13 +42,8 @@ export const useFractalGuardContracts = ({ loadOnMount = true }: { loadOnMount?: if (!baseContracts) { return; } - const { - freezeERC20VotingMasterCopyContract, - freezeERC721VotingMasterCopyContract, - freezeMultisigVotingMasterCopyContract, - azoriusFreezeGuardMasterCopyContract, - multisigFreezeGuardMasterCopyContract, - } = baseContracts; + const { azoriusFreezeGuardMasterCopyContract, multisigFreezeGuardMasterCopyContract } = + baseContracts; const { guard } = _safe; let freezeGuardContract: @@ -81,25 +83,9 @@ export const useFractalGuardContracts = ({ loadOnMount = true }: { loadOnMount?: ? FreezeVotingType.ERC721 : FreezeVotingType.ERC20; - const freezeVotingContract = - freezeVotingType === FreezeVotingType.MULTISIG - ? { - asSigner: freezeMultisigVotingMasterCopyContract.asSigner.attach(votingAddress), - asProvider: freezeMultisigVotingMasterCopyContract.asProvider.attach(votingAddress), - } - : freezeVotingType === FreezeVotingType.ERC721 - ? { - asSigner: freezeERC721VotingMasterCopyContract.asSigner.attach(votingAddress), - asProvider: freezeERC721VotingMasterCopyContract.asProvider.attach(votingAddress), - } - : { - asSigner: freezeERC20VotingMasterCopyContract.asSigner.attach(votingAddress), - asProvider: freezeERC20VotingMasterCopyContract.asProvider.attach(votingAddress), - }; - const contracts = { - freezeGuardContract, - freezeVotingContract, + freezeGuardContractAddress: freezeGuardContract.asProvider.address, + freezeVotingContractAddress: votingAddress, freezeVotingType, freezeGuardType, }; @@ -118,13 +104,20 @@ export const useFractalGuardContracts = ({ loadOnMount = true }: { loadOnMount?: }, [action, daoAddress, safe, fractalModules, loadFractalGuardContracts]); useEffect(() => { - if (daoAddress && chainId + daoAddress !== loadKey.current && loadOnMount && isModulesLoaded) { - loadKey.current = chainId + daoAddress; + if ( + parentAddress && + daoAddress && + parentAddress + daoAddress !== loadKey.current && + loadOnMount && + isModulesLoaded + ) { + loadKey.current = parentAddress + daoAddress; + console.count('load guard contracts'); setGuardContracts(); } if (!daoAddress) { loadKey.current = undefined; } - }, [setGuardContracts, isModulesLoaded, daoAddress, loadOnMount, chainId]); + }, [setGuardContracts, isModulesLoaded, parentAddress, loadOnMount, chainId, daoAddress]); return loadFractalGuardContracts; }; From 2d86a71238455971c2d292212b76c67e7c91dbb2 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Mar 2024 02:34:37 -0400 Subject: [PATCH 03/30] fix bug thats been bugging me - no conditional preventing when not on ERC721 DAO --- src/hooks/DAO/proposal/useUserERC721VotingTokens.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts b/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts index 5192962dcf..5af878a5f8 100644 --- a/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts +++ b/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts @@ -213,10 +213,10 @@ export default function useUserERC721VotingTokens( }, [getUserERC721VotingTokens, proposalId, safeAddress]); useEffect(() => { - if (loadOnMount) { + if (loadOnMount && erc721LinearVotingContractAddress) { loadUserERC721VotingTokens(); } - }, [loadUserERC721VotingTokens, loadOnMount]); + }, [loadUserERC721VotingTokens, loadOnMount, erc721LinearVotingContractAddress]); return { totalVotingTokenIds, From 9cc83635308f62e79ff63fb50ceca9f4563b9918 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Mar 2024 02:35:15 -0400 Subject: [PATCH 04/30] pretty/lint --- src/hooks/DAO/loaders/useFractalFreeze.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/hooks/DAO/loaders/useFractalFreeze.ts b/src/hooks/DAO/loaders/useFractalFreeze.ts index 9ef0cc7b3e..8826e70fd3 100644 --- a/src/hooks/DAO/loaders/useFractalFreeze.ts +++ b/src/hooks/DAO/loaders/useFractalFreeze.ts @@ -32,10 +32,7 @@ export const useFractalFreeze = ({ const loadKey = useRef(); const isFreezeSet = useRef(false); - const { - guardContracts, - action, - } = useFractal(); + const { guardContracts, action } = useFractal(); const baseContracts = useSafeContracts(); const { address: account } = useAccount(); const { getUserERC721VotingTokens } = useUserERC721VotingTokens( @@ -169,7 +166,7 @@ export const useFractalFreeze = ({ loadOnMount && guardContracts.freezeVotingContractAddress + parentSafeAddress !== loadKey.current ) { - console.count('load freeze guard') + console.count('load freeze guard'); setFractalFreezeGuard(guardContracts); loadKey.current = guardContracts.freezeVotingContractAddress + parentSafeAddress; } From 5b8075a1a5560ae670000b361ad959be9c88029b Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Mar 2024 02:39:36 -0400 Subject: [PATCH 05/30] fix Parent Link --- src/components/pages/DaoDashboard/Info/ParentLink.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/pages/DaoDashboard/Info/ParentLink.tsx b/src/components/pages/DaoDashboard/Info/ParentLink.tsx index 0a9d4b4fa9..09b696e7a0 100644 --- a/src/components/pages/DaoDashboard/Info/ParentLink.tsx +++ b/src/components/pages/DaoDashboard/Info/ParentLink.tsx @@ -22,7 +22,7 @@ export function ParentLink() { Date: Fri, 22 Mar 2024 12:21:03 -0400 Subject: [PATCH 06/30] fix navigating to other DAO via DAO menu --- src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx index 70d3217520..da5bb0a249 100644 --- a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx +++ b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx @@ -124,7 +124,7 @@ export function ManageDAOMenu({ }, [fractalNode, safe, safeAddress, type, getZodiacModuleProxyMasterCopyData, baseContracts]); const handleNavigateToSettings = useCallback( - () => navigate(DAO_ROUTES.settings.relative(safeAddress)), + () => navigate(DAO_ROUTES.settings.relative(safeAddress), { replace: true }), [navigate, safeAddress], ); @@ -133,7 +133,7 @@ export function ManageDAOMenu({ const options = useMemo(() => { const createSubDAOOption = { optionKey: 'optionCreateSubDAO', - onClick: () => navigate(DAO_ROUTES.newSubDao.relative(safeAddress)), + onClick: () => navigate(DAO_ROUTES.newSubDao.relative(safeAddress), { replace: true }), }; const freezeOption = { From f3cee31dadb30eb6b2fba71db813c46ee0e17092 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Mar 2024 12:22:04 -0400 Subject: [PATCH 07/30] clean up loading conditional --- src/hooks/DAO/loaders/useFractalGuardContracts.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/hooks/DAO/loaders/useFractalGuardContracts.ts b/src/hooks/DAO/loaders/useFractalGuardContracts.ts index 716ccd919b..db133d02b3 100644 --- a/src/hooks/DAO/loaders/useFractalGuardContracts.ts +++ b/src/hooks/DAO/loaders/useFractalGuardContracts.ts @@ -105,17 +105,15 @@ export const useFractalGuardContracts = ({ loadOnMount = true }: { loadOnMount?: useEffect(() => { if ( - parentAddress && - daoAddress && - parentAddress + daoAddress !== loadKey.current && - loadOnMount && + loadOnMount && parentAddress && daoAddress && + parentAddress + daoAddress + chainId !== loadKey.current && isModulesLoaded ) { - loadKey.current = parentAddress + daoAddress; - console.count('load guard contracts'); + loadKey.current = parentAddress + daoAddress + chainId; setGuardContracts(); } - if (!daoAddress) { + + if (!daoAddress || !parentAddress) { loadKey.current = undefined; } }, [setGuardContracts, isModulesLoaded, parentAddress, loadOnMount, chainId, daoAddress]); From 8db23c4bd8b58deaf5be8ae90f13a872e3331fc4 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Mar 2024 15:56:16 -0400 Subject: [PATCH 08/30] fix verical node line --- src/components/pages/DaoHierarchy/NodeLines.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/pages/DaoHierarchy/NodeLines.tsx b/src/components/pages/DaoHierarchy/NodeLines.tsx index 1e7e29c1bd..73bc1eb2f0 100644 --- a/src/components/pages/DaoHierarchy/NodeLines.tsx +++ b/src/components/pages/DaoHierarchy/NodeLines.tsx @@ -25,7 +25,6 @@ export function NodeLineVertical({ isCurrentDAO, trueDepth = 0, lastChildDescend position="absolute" h={`calc(100% - ${NODE_HEIGHT_REM + NODE_MARGIN_TOP_REM}rem)`} w="100%" - zIndex={-1 - trueDepth} bottom={0} overflow="hidden" > From ea0479dcf0a2f46830ed418017efc45cb3ad1c19 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Mar 2024 15:56:43 -0400 Subject: [PATCH 09/30] remove unneeded dependency --- src/components/pages/DaoHierarchy/DaoNode.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/pages/DaoHierarchy/DaoNode.tsx b/src/components/pages/DaoHierarchy/DaoNode.tsx index 0712fa8899..1011d11c47 100644 --- a/src/components/pages/DaoHierarchy/DaoNode.tsx +++ b/src/components/pages/DaoHierarchy/DaoNode.tsx @@ -38,7 +38,7 @@ export function DaoNode({ const isCurrentDAO = daoAddress === currentDAOAddress; useEffect(() => { - if (daoAddress && !fractalNode) { + if (daoAddress) { loadDao(utils.getAddress(daoAddress)).then(_node => { const errorNode = _node as WithError; if (!errorNode.error) { @@ -80,7 +80,8 @@ export function DaoNode({ } }); } - }, [loadDao, daoAddress, fractalNode, depth]); + }, [loadDao, daoAddress, depth]); + return ( From f1340dee6afacd6d09f6eb4941adf12dc3e94933 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Mar 2024 15:58:00 -0400 Subject: [PATCH 10/30] fix subDAO loading issues --- .../governance/useERC20LinearStrategy.ts | 13 +++--- .../governance/useERC721LinearStrategy.ts | 13 +++--- src/hooks/DAO/loaders/useFractalFreeze.ts | 1 - src/hooks/DAO/loaders/useFractalGovernance.ts | 36 ++++++--------- .../DAO/loaders/useFractalGuardContracts.ts | 44 ++++++++++++------- src/hooks/DAO/loaders/useFractalNode.ts | 2 +- src/hooks/DAO/loaders/useProposals.ts | 1 + .../daos/[daoAddress]/hierarchy/index.tsx | 11 +++-- src/providers/App/guardContracts/action.ts | 2 +- src/providers/App/guardContracts/reducer.ts | 2 +- src/providers/App/node/reducer.ts | 4 +- src/types/fractal.ts | 4 +- 12 files changed, 73 insertions(+), 60 deletions(-) diff --git a/src/hooks/DAO/loaders/governance/useERC20LinearStrategy.ts b/src/hooks/DAO/loaders/governance/useERC20LinearStrategy.ts index 4b17f90dc6..049a774be3 100644 --- a/src/hooks/DAO/loaders/governance/useERC20LinearStrategy.ts +++ b/src/hooks/DAO/loaders/governance/useERC20LinearStrategy.ts @@ -14,6 +14,7 @@ import { useTimeHelpers } from '../../../utils/useTimeHelpers'; export const useERC20LinearStrategy = () => { const { + governance: { type }, governanceContracts: { ozLinearVotingContractAddress, azoriusContractAddress }, action, } = useFractal(); @@ -67,7 +68,7 @@ export const useERC20LinearStrategy = () => { ]); useEffect(() => { - if (!ozLinearVotingContractAddress || !baseContracts) { + if (!ozLinearVotingContractAddress || !baseContracts || !type) { return; } const ozLinearVotingContract = baseContracts.linearVotingMasterCopyContract.asProvider.attach( @@ -85,10 +86,10 @@ export const useERC20LinearStrategy = () => { return () => { ozLinearVotingContract.off(votingPeriodfilter, listener); }; - }, [ozLinearVotingContractAddress, action, baseContracts]); + }, [ozLinearVotingContractAddress, action, baseContracts, type]); useEffect(() => { - if (!ozLinearVotingContractAddress || !baseContracts) { + if (!ozLinearVotingContractAddress || !baseContracts || !type) { return; } const ozLinearVotingContract = baseContracts.linearVotingMasterCopyContract.asProvider.attach( @@ -107,10 +108,10 @@ export const useERC20LinearStrategy = () => { return () => { ozLinearVotingContract.off(quorumNumeratorUpdatedFilter, quorumNumeratorUpdatedListener); }; - }, [ozLinearVotingContractAddress, action, baseContracts]); + }, [ozLinearVotingContractAddress, action, baseContracts, type]); useEffect(() => { - if (!azoriusContractAddress || !baseContracts) { + if (!azoriusContractAddress || !baseContracts || !type) { return; } const azoriusContract = @@ -126,7 +127,7 @@ export const useERC20LinearStrategy = () => { return () => { azoriusContract.off(timeLockPeriodFilter, timelockPeriodListener); }; - }, [azoriusContractAddress, action, baseContracts]); + }, [azoriusContractAddress, action, baseContracts, type]); return loadERC20Strategy; }; diff --git a/src/hooks/DAO/loaders/governance/useERC721LinearStrategy.ts b/src/hooks/DAO/loaders/governance/useERC721LinearStrategy.ts index 97012eab3a..0a61a6e3a4 100644 --- a/src/hooks/DAO/loaders/governance/useERC721LinearStrategy.ts +++ b/src/hooks/DAO/loaders/governance/useERC721LinearStrategy.ts @@ -17,6 +17,7 @@ import { useTimeHelpers } from '../../../utils/useTimeHelpers'; export const useERC721LinearStrategy = () => { const { governanceContracts: { erc721LinearVotingContractAddress, azoriusContractAddress }, + governance: {type}, action, } = useFractal(); const provider = useEthersProvider(); @@ -72,7 +73,7 @@ export const useERC721LinearStrategy = () => { ]); useEffect(() => { - if (!erc721LinearVotingContractAddress || !baseContracts) { + if (!erc721LinearVotingContractAddress || !baseContracts || !type) { return; } const erc721LinearVotingContract = @@ -90,10 +91,10 @@ export const useERC721LinearStrategy = () => { return () => { erc721LinearVotingContract.off(votingPeriodfilter, listener); }; - }, [erc721LinearVotingContractAddress, action, baseContracts]); + }, [erc721LinearVotingContractAddress, action, baseContracts, type]); useEffect(() => { - if (!erc721LinearVotingContractAddress || !baseContracts) { + if (!erc721LinearVotingContractAddress || !baseContracts || !type) { return; } const erc721LinearVotingContract = @@ -114,10 +115,10 @@ export const useERC721LinearStrategy = () => { return () => { erc721LinearVotingContract.off(quorumThresholdUpdatedFilter, quorumThresholdUpdatedListener); }; - }, [erc721LinearVotingContractAddress, action, baseContracts]); + }, [erc721LinearVotingContractAddress, action, baseContracts, type]); useEffect(() => { - if (!azoriusContractAddress || !baseContracts) { + if (!azoriusContractAddress || !baseContracts || !type) { return; } const azoriusContract = @@ -134,7 +135,7 @@ export const useERC721LinearStrategy = () => { return () => { azoriusContract.off(timeLockPeriodFilter, timelockPeriodListener); }; - }, [azoriusContractAddress, action, baseContracts]); + }, [azoriusContractAddress, action, baseContracts, type]); return loadERC721Strategy; }; diff --git a/src/hooks/DAO/loaders/useFractalFreeze.ts b/src/hooks/DAO/loaders/useFractalFreeze.ts index 8826e70fd3..0937f777fa 100644 --- a/src/hooks/DAO/loaders/useFractalFreeze.ts +++ b/src/hooks/DAO/loaders/useFractalFreeze.ts @@ -166,7 +166,6 @@ export const useFractalFreeze = ({ loadOnMount && guardContracts.freezeVotingContractAddress + parentSafeAddress !== loadKey.current ) { - console.count('load freeze guard'); setFractalFreezeGuard(guardContracts); loadKey.current = guardContracts.freezeVotingContractAddress + parentSafeAddress; } diff --git a/src/hooks/DAO/loaders/useFractalGovernance.ts b/src/hooks/DAO/loaders/useFractalGovernance.ts index 9355b5f881..33e1bebaf1 100644 --- a/src/hooks/DAO/loaders/useFractalGovernance.ts +++ b/src/hooks/DAO/loaders/useFractalGovernance.ts @@ -18,11 +18,11 @@ export const useFractalGovernance = () => { const loadKey = useRef(); const { - node: { daoAddress, nodeHierarchy }, + node: { daoAddress }, governanceContracts, action, - guardContracts, governance: { type }, + guardContracts: { isGuardLoaded }, } = useFractal(); const loadDAOProposals = useDAOProposals(); @@ -68,7 +68,7 @@ export const useFractalGovernance = () => { }, context: { chainName: subgraphChainName }, pollInterval: ONE_MINUTE, - skip: !daoAddress, + skip: !daoAddress || !type, }); useEffect(() => { @@ -80,14 +80,7 @@ export const useFractalGovernance = () => { ozLinearVotingContractAddress, } = governanceContracts; - const newLoadKey = - (azoriusContractAddress ? '1' : '0') + - nodeHierarchy.parentAddress + - !!guardContracts.freezeGuardContractAddress; - - if (isLoaded && newLoadKey !== loadKey.current) { - loadKey.current = newLoadKey; - + if (isLoaded && !type) { if (azoriusContractAddress) { if (ozLinearVotingContractAddress) { action.dispatch({ @@ -115,30 +108,27 @@ export const useFractalGovernance = () => { }); } } - if (!daoAddress) { - loadKey.current = undefined; - } + }, [ governanceContracts, - loadDAOProposals, loadUnderlyingERC20Token, loadERC20Strategy, loadERC20Token, loadLockedVotesToken, - nodeHierarchy.parentAddress, - guardContracts.freezeGuardContractAddress, loadERC721Strategy, loadERC721Tokens, action, - daoAddress, + type ]); useEffect(() => { - if (type) { - // Since previous hook is the only place where governance type is set - we don't need any additional check - // But it's better to be sure that governance type is defined before calling for proposals loading + const newLoadKey = (daoAddress || "0x"); + if (type && daoAddress && daoAddress !== loadKey.current && isGuardLoaded) { + loadKey.current = newLoadKey; loadDAOProposals(); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [type]); + if(!type || !daoAddress) { + loadKey.current = undefined; + } + }, [type, loadDAOProposals, isGuardLoaded, daoAddress]); }; diff --git a/src/hooks/DAO/loaders/useFractalGuardContracts.ts b/src/hooks/DAO/loaders/useFractalGuardContracts.ts index db133d02b3..dbffcef7bb 100644 --- a/src/hooks/DAO/loaders/useFractalGuardContracts.ts +++ b/src/hooks/DAO/loaders/useFractalGuardContracts.ts @@ -10,6 +10,7 @@ import { FreezeGuardType, FreezeVotingType, } from '../../../types'; +import useSafeContracts from '../../safe/useSafeContracts'; import { useMasterCopy } from '../../utils/useMasterCopy'; import { FractalModuleData, FractalModuleType } from './../../../types/fractal'; @@ -17,17 +18,11 @@ export const useFractalGuardContracts = ({ loadOnMount = true }: { loadOnMount?: // load key for component; helps prevent unnecessary calls const loadKey = useRef(); const { - node: { - daoAddress, - safe, - fractalModules, - isModulesLoaded, - nodeHierarchy: { parentAddress }, - }, + node: { daoAddress, safe, fractalModules, isHierarchyLoaded }, - baseContracts, action, } = useFractal(); + const baseContracts = useSafeContracts() const { chainId } = useNetworkConfig(); @@ -56,7 +51,16 @@ export const useFractalGuardContracts = ({ loadOnMount = true }: { loadOnMount?: ); if (!!azoriusModule && azoriusModule.moduleContract) { const azoriusGuardAddress = await azoriusModule.moduleContract.getGuard(); - if (azoriusGuardAddress === constants.AddressZero) return; + + if (azoriusGuardAddress === constants.AddressZero) { + return { + freezeGuardContractAddress: '', + freezeVotingContractAddress: '', + freezeVotingType: null, + freezeGuardType: null, + }; + } + freezeGuardContract = { asSigner: azoriusFreezeGuardMasterCopyContract.asSigner.attach(azoriusGuardAddress), asProvider: azoriusFreezeGuardMasterCopyContract.asProvider.attach(azoriusGuardAddress), @@ -91,13 +95,20 @@ export const useFractalGuardContracts = ({ loadOnMount = true }: { loadOnMount?: }; return contracts; + } else { + return { + freezeGuardContractAddress: '', + freezeVotingContractAddress: '', + freezeVotingType: null, + freezeGuardType: null, + }; } }, [baseContracts, getZodiacModuleProxyMasterCopyData], ); const setGuardContracts = useCallback(async () => { - if (!daoAddress || !safe || !fractalModules.length || !safe.guard) return; + if (!daoAddress || !safe) return; const contracts = await loadFractalGuardContracts(daoAddress, safe, fractalModules); if (!contracts) return; action.dispatch({ type: GuardContractAction.SET_GUARD_CONTRACT, payload: contracts }); @@ -105,17 +116,18 @@ export const useFractalGuardContracts = ({ loadOnMount = true }: { loadOnMount?: useEffect(() => { if ( - loadOnMount && parentAddress && daoAddress && - parentAddress + daoAddress + chainId !== loadKey.current && - isModulesLoaded + loadOnMount && + daoAddress && + daoAddress + chainId !== loadKey.current && + isHierarchyLoaded && safe ) { - loadKey.current = parentAddress + daoAddress + chainId; + loadKey.current = daoAddress + chainId; setGuardContracts(); } - if (!daoAddress || !parentAddress) { + if (!daoAddress) { loadKey.current = undefined; } - }, [setGuardContracts, isModulesLoaded, parentAddress, loadOnMount, chainId, daoAddress]); + }, [setGuardContracts, isHierarchyLoaded, loadOnMount, chainId, daoAddress, safe]); return loadFractalGuardContracts; }; diff --git a/src/hooks/DAO/loaders/useFractalNode.ts b/src/hooks/DAO/loaders/useFractalNode.ts index e50f5d1865..8e5afc7157 100644 --- a/src/hooks/DAO/loaders/useFractalNode.ts +++ b/src/hooks/DAO/loaders/useFractalNode.ts @@ -99,7 +99,6 @@ export const useFractalNode = ({ daoAddress }: { daoAddress?: string }) => { action.resetDAO(); setErrorLoading(true); } else { - currentValidSafe.current = _chainId + _daoAddress; action.dispatch({ type: NodeAction.SET_FRACTAL_MODULES, payload: await lookupModules(safeInfo.modules), @@ -133,6 +132,7 @@ export const useFractalNode = ({ daoAddress }: { daoAddress?: string }) => { if (daoAddress && chainId + daoAddress !== currentValidSafe.current) { setNodeLoading(true); setDAO(chainId, daoAddress); + currentValidSafe.current = chainId + daoAddress } if (!daoAddress) { currentValidSafe.current = undefined; diff --git a/src/hooks/DAO/loaders/useProposals.ts b/src/hooks/DAO/loaders/useProposals.ts index 245365fa3a..482af484fd 100644 --- a/src/hooks/DAO/loaders/useProposals.ts +++ b/src/hooks/DAO/loaders/useProposals.ts @@ -28,6 +28,7 @@ export const useDAOProposals = () => { clearIntervals(); } else if (type === GovernanceType.MULTISIG) { // load mulisig proposals + clearIntervals(); setMethodOnInterval(loadSafeMultisigProposals); } }, [ diff --git a/src/pages/daos/[daoAddress]/hierarchy/index.tsx b/src/pages/daos/[daoAddress]/hierarchy/index.tsx index b24bc10323..d9b859e2aa 100644 --- a/src/pages/daos/[daoAddress]/hierarchy/index.tsx +++ b/src/pages/daos/[daoAddress]/hierarchy/index.tsx @@ -8,11 +8,16 @@ import { useFractal } from '../../../../providers/App/AppProvider'; export default function HierarchyPage() { const { - node: { daoAddress, daoName, nodeHierarchy }, + node: { + daoAddress, + daoName, + nodeHierarchy: { parentAddress }, + isHierarchyLoaded, + }, } = useFractal(); const { t } = useTranslation('breadcrumbs'); - if (!daoAddress) { + if (!daoAddress || !isHierarchyLoaded) { return (
@@ -35,7 +40,7 @@ export default function HierarchyPage() { ]} /> diff --git a/src/providers/App/guardContracts/action.ts b/src/providers/App/guardContracts/action.ts index db31db3646..3451394e20 100644 --- a/src/providers/App/guardContracts/action.ts +++ b/src/providers/App/guardContracts/action.ts @@ -6,5 +6,5 @@ export enum GuardContractAction { export type GuardContractActions = { type: GuardContractAction.SET_GUARD_CONTRACT; - payload: FractalGuardContracts; + payload: Omit; }; diff --git a/src/providers/App/guardContracts/reducer.ts b/src/providers/App/guardContracts/reducer.ts index b2d528efee..21905c5501 100644 --- a/src/providers/App/guardContracts/reducer.ts +++ b/src/providers/App/guardContracts/reducer.ts @@ -14,7 +14,7 @@ export const guardContractReducer = ( ) => { switch (action.type) { case GuardContractAction.SET_GUARD_CONTRACT: - return action.payload; + return {...action.payload, isGuardLoaded: true}; default: return state; } diff --git a/src/providers/App/node/reducer.ts b/src/providers/App/node/reducer.ts index e10a7a0489..ad67816d3b 100644 --- a/src/providers/App/node/reducer.ts +++ b/src/providers/App/node/reducer.ts @@ -13,6 +13,8 @@ export const initialNodeState: FractalNode = { safe: null, fractalModules: [], nodeHierarchy: initialNodeHierarchyState, + isHierarchyLoaded: false, + isModulesLoaded: false, }; export function nodeReducer(state: FractalNode, action: NodeActions) { @@ -25,7 +27,7 @@ export function nodeReducer(state: FractalNode, action: NodeActions) { }; } case NodeAction.SET_DAO_INFO: { - return { ...state, ...action.payload }; + return { ...state, ...action.payload, isHierarchyLoaded: true }; } case NodeAction.SET_FRACTAL_MODULES: { return { ...state, fractalModules: action.payload, isModulesLoaded: true }; diff --git a/src/types/fractal.ts b/src/types/fractal.ts index 3bfc34debb..0b1cb964ed 100644 --- a/src/types/fractal.ts +++ b/src/types/fractal.ts @@ -238,11 +238,12 @@ export interface FractalNode { fractalModules: FractalModuleData[]; nodeHierarchy: NodeHierarchy; isModulesLoaded?: boolean; + isHierarchyLoaded?: boolean; daoSnapshotURL?: string; proposalTemplatesHash?: string; } -export interface Node extends Omit {} +export interface Node extends Omit {} export interface FractalModuleData { moduleContract: Azorius | FractalModule | undefined; @@ -260,6 +261,7 @@ export interface FractalGuardContracts { freezeVotingContractAddress?: string; freezeGuardType: FreezeGuardType | null; freezeVotingType: FreezeVotingType | null; + isGuardLoaded?: boolean; } export interface FreezeGuard { From 0ab0007c271de031016d86a3fe603541761174a8 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Mar 2024 15:58:20 -0400 Subject: [PATCH 11/30] Fix timelock execution and timer --- .../ui/proposal/useProposalCountdown.tsx | 172 ++++++++++-------- src/hooks/utils/useSafeTransactions.ts | 15 +- 2 files changed, 100 insertions(+), 87 deletions(-) diff --git a/src/components/ui/proposal/useProposalCountdown.tsx b/src/components/ui/proposal/useProposalCountdown.tsx index 8c5e153ed1..b4ae3d837c 100644 --- a/src/components/ui/proposal/useProposalCountdown.tsx +++ b/src/components/ui/proposal/useProposalCountdown.tsx @@ -1,6 +1,6 @@ import { AzoriusFreezeGuard, MultisigFreezeGuard } from '@fractal-framework/fractal-contracts'; import { BigNumber } from 'ethers'; -import { useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { logError } from '../../../helpers/errorLogging'; import useSnapshotProposal from '../../../hooks/DAO/loaders/snapshot/useSnapshotProposal'; import { useDAOProposals } from '../../../hooks/DAO/loaders/useProposals'; @@ -41,6 +41,7 @@ export function useProposalCountdown(proposal: FractalProposal) { }); let updateStateInterval = useRef(); + let countdownInterval = useRef(); useEffect(() => { // if it's not a state that requires a countdown, clear the interval and return if ( @@ -85,93 +86,106 @@ export function useProposalCountdown(proposal: FractalProposal) { }; }, [secondsLeft, loadDAOProposals, proposal, updateProposalState, governance.type, dao]); - useEffect(() => { - let countdownInterval: NodeJS.Timer | undefined; - - // continually calculates the initial time (in ms) - the current time (in ms) - // then converts it to seconds, all on a 1 second interval - const startCountdown = (initialTimeMs: number) => { - countdownInterval = setInterval(() => { - setSecondsLeft(Math.floor((initialTimeMs - Date.now()) / 1000)); - }, 1000); - }; - - async function getCountdown() { - const freezeGuard = - freezeGuardType === FreezeGuardType.MULTISIG - ? baseContracts?.multisigFreezeGuardMasterCopyContract.asProvider - : freezeGuardType === FreezeGuardType.AZORIUS - ? (baseContracts?.azoriusFreezeGuardMasterCopyContract.asProvider as AzoriusFreezeGuard) - : undefined; - - const isSafeGuard = freezeGuardType === FreezeGuardType.MULTISIG; - const isAzoriusGuard = freezeGuardType === FreezeGuardType.AZORIUS; - - const timeLockPeriod = azoriusGovernance.votingStrategy?.timeLockPeriod; - const votingDeadlineMs = (proposal as AzoriusProposal).deadlineMs; - - // If the proposal is active and has a deadline, start the countdown (for Azorius proposals) - if (proposal.state === FractalProposalState.ACTIVE && votingDeadlineMs) { - startCountdown(votingDeadlineMs); - } else if ( - // If the proposal is timelocked and has a deadline, start the countdown (for Azorius proposals) - proposal.state === FractalProposalState.TIMELOCKED && - votingDeadlineMs && - timeLockPeriod - ) { - startCountdown(votingDeadlineMs + Number(timeLockPeriod.value) * 1000); - // If the proposal is timelocked start the countdown (for safe multisig proposals with guards) - } else if ( - proposal.state === FractalProposalState.TIMELOCKED && - freezeGuard && - isSafeGuard && - provider - ) { + const startCountdown = useCallback((initialTimeMs: number) => { + countdownInterval.current = setInterval(() => { + setSecondsLeft(Math.floor((initialTimeMs - Date.now()) / 1000)); + }, 1000); + }, []); + + const getCountdown = useCallback(async () => { + if (!baseContracts) return; + const freezeGuard = + freezeGuardType === FreezeGuardType.MULTISIG + ? baseContracts.multisigFreezeGuardMasterCopyContract.asSigner.attach( + freezeGuardContractAddress || '0x', + ) + : freezeGuardType === FreezeGuardType.AZORIUS + ? (baseContracts.azoriusFreezeGuardMasterCopyContract.asSigner.attach( + freezeGuardContractAddress || '0x', + ) as AzoriusFreezeGuard) + : undefined; + + const isSafeGuard = freezeGuardType === FreezeGuardType.MULTISIG; + const isAzoriusGuard = freezeGuardType === FreezeGuardType.AZORIUS; + + const timeLockPeriod = azoriusGovernance.votingStrategy?.timeLockPeriod; + const votingDeadlineMs = (proposal as AzoriusProposal).deadlineMs; + + // If the proposal is active and has a deadline, start the countdown (for Azorius proposals) + if (proposal.state === FractalProposalState.ACTIVE && votingDeadlineMs) { + startCountdown(votingDeadlineMs); + return; + } else if ( + // If the proposal is timelocked and has a deadline, start the countdown (for Azorius proposals) + proposal.state === FractalProposalState.TIMELOCKED && + votingDeadlineMs && + timeLockPeriod + ) { + startCountdown(votingDeadlineMs + Number(timeLockPeriod.value) * 1000); + // If the proposal is timelocked start the countdown (for safe multisig proposals with guards) + return; + } else if ( + proposal.state === FractalProposalState.TIMELOCKED && + freezeGuard && + isSafeGuard && + provider + ) { + const safeGuard = freezeGuard as MultisigFreezeGuard; + const timelockedTimestamp = await getTxTimelockedTimestamp(proposal, safeGuard, provider); + const guardTimeLockPeriod = await blocksToSeconds(await safeGuard.timelockPeriod(), provider); + startCountdown(timelockedTimestamp * 1000 + guardTimeLockPeriod * 1000); + // If the proposal is executable start the countdown (for safe multisig proposals with guards) + return; + } else if (proposal.state === FractalProposalState.EXECUTABLE && freezeGuard) { + let guardTimelockPeriod: number = 0; + if (isSafeGuard && provider) { const safeGuard = freezeGuard as MultisigFreezeGuard; - const timelockedTimestamp = await getTxTimelockedTimestamp(proposal, safeGuard, provider); - const guardTimeLockPeriod = await blocksToSeconds( - await safeGuard.timelockPeriod(), - provider, - ); - startCountdown(timelockedTimestamp * 1000 + guardTimeLockPeriod * 1000); - // If the proposal is executable start the countdown (for safe multisig proposals with guards) - } else if (proposal.state === FractalProposalState.EXECUTABLE && freezeGuard) { - let guardTimelockPeriod: number = 0; - if (isSafeGuard && provider) { - const safeGuard = freezeGuard as MultisigFreezeGuard; - const timelockedTimestamp = - (await getTxTimelockedTimestamp(proposal, safeGuard, provider)) * 1000; - const safeGuardTimelockPeriod = - (await blocksToSeconds(await safeGuard.timelockPeriod(), provider)) * 1000; - const guardExecutionPeriod = - (await blocksToSeconds(await safeGuard.executionPeriod(), provider)) * 1000; - guardTimelockPeriod = - timelockedTimestamp + safeGuardTimelockPeriod + guardExecutionPeriod; - - // If the proposal is executing start the countdown (for Azorius proposals with guards) - } else if (isAzoriusGuard && timeLockPeriod && votingDeadlineMs) { - guardTimelockPeriod = timeLockPeriod.value.toNumber() * 1000 + votingDeadlineMs; - } - startCountdown(guardTimelockPeriod); - } else if (isSnapshotProposal && proposal.state === FractalProposalState.PENDING) { - startCountdown(snapshotProposal.startTime * 1000); - } else if (isSnapshotProposal) { - startCountdown(snapshotProposal.endTime * 1000); + const timelockedTimestamp = + (await getTxTimelockedTimestamp(proposal, safeGuard, provider)) * 1000; + const safeGuardTimelockPeriod = + (await blocksToSeconds(await safeGuard.timelockPeriod(), provider)) * 1000; + const guardExecutionPeriod = + (await blocksToSeconds(await safeGuard.executionPeriod(), provider)) * 1000; + guardTimelockPeriod = timelockedTimestamp + safeGuardTimelockPeriod + guardExecutionPeriod; + + // If the proposal is executing start the countdown (for Azorius proposals with guards) + return; + } else if (isAzoriusGuard && timeLockPeriod && votingDeadlineMs) { + guardTimelockPeriod = timeLockPeriod.value.toNumber() * 1000 + votingDeadlineMs; } + startCountdown(guardTimelockPeriod); + return; + } else if (isSnapshotProposal && proposal.state === FractalProposalState.PENDING) { + startCountdown(snapshotProposal.startTime * 1000); + return; + } else if (isSnapshotProposal) { + startCountdown(snapshotProposal.endTime * 1000); + return; } + }, [ + freezeGuardType, + baseContracts, + azoriusGovernance.votingStrategy, + provider, + proposal, + startCountdown, + isSnapshotProposal, + snapshotProposal, + freezeGuardContractAddress, + ]); + + useEffect(() => { + if (!baseContracts) return; + // continually calculates the initial time (in ms) - the current time (in ms) + // then converts it to seconds, all on a 1 second interval getCountdown(); return () => { - clearInterval(countdownInterval); + clearInterval(countdownInterval.current); }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - proposal.state, - azoriusGovernance.votingStrategy, - freezeGuardContractAddress, - freezeGuardType, - ]); + }, [proposal.state, azoriusGovernance.votingStrategy, freezeGuardType, baseContracts]); return secondsLeft; } diff --git a/src/hooks/utils/useSafeTransactions.ts b/src/hooks/utils/useSafeTransactions.ts index e9c46c08ed..3d0bf3cd0c 100644 --- a/src/hooks/utils/useSafeTransactions.ts +++ b/src/hooks/utils/useSafeTransactions.ts @@ -320,20 +320,19 @@ export const useSafeTransactions = () => { let freezeGuard: MultisigFreezeGuard | undefined; let freezeGuardData: FreezeGuardData | undefined; + if (guardContracts.freezeGuardContractAddress && baseContracts) { const blockNumber = await provider.getBlockNumber(); - const averageBlockTime = await getAverageBlockTime(provider); - + const averageBlockTime = BigNumber.from(Math.round(await getAverageBlockTime(provider))); freezeGuard = baseContracts.multisigFreezeGuardMasterCopyContract.asSigner.attach( guardContracts.freezeGuardContractAddress, ); + + const timelockPeriod = BigNumber.from(await freezeGuard.timelockPeriod()); + const executionPeriod = BigNumber.from(await freezeGuard.executionPeriod()); freezeGuardData = { - guardTimelockPeriodMs: BigNumber.from( - (await freezeGuard.timelockPeriod()) * averageBlockTime * 1000, - ), - guardExecutionPeriodMs: BigNumber.from( - (await freezeGuard.executionPeriod()) * averageBlockTime * 1000, - ), + guardTimelockPeriodMs: BigNumber.from(timelockPeriod.mul(averageBlockTime).mul(1000)), + guardExecutionPeriodMs: BigNumber.from(executionPeriod.mul(averageBlockTime).mul(1000)), lastBlock: await provider.getBlock(blockNumber), }; } From bfb199a92e74c8d8717f80127433d1b90db42cba Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Mar 2024 15:59:23 -0400 Subject: [PATCH 12/30] fix display data --- src/components/pages/DaoDashboard/Info/InfoGovernance.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/pages/DaoDashboard/Info/InfoGovernance.tsx b/src/components/pages/DaoDashboard/Info/InfoGovernance.tsx index 20bb18f4c9..122f838270 100644 --- a/src/components/pages/DaoDashboard/Info/InfoGovernance.tsx +++ b/src/components/pages/DaoDashboard/Info/InfoGovernance.tsx @@ -33,7 +33,7 @@ export function InfoGovernance() { if (freezeGuardType == FreezeGuardType.MULTISIG) { if (freezeGuardContractAddress && baseContracts) { const freezeGuardContract = - baseContracts.multisigFreezeGuardMasterCopyContract.asProvider; + baseContracts.multisigFreezeGuardMasterCopyContract.asProvider.attach(freezeGuardContractAddress); setTimelockPeriod(await formatBlocks(await freezeGuardContract.timelockPeriod())); setExecutionPeriod(await formatBlocks(await freezeGuardContract.executionPeriod())); } From 0529690b7254d1c53517efe6de4ef0f897b16aa9 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Mar 2024 16:05:51 -0400 Subject: [PATCH 13/30] run pretty/lint --- src/components/pages/DaoDashboard/Info/InfoGovernance.tsx | 4 +++- src/components/pages/DaoHierarchy/DaoNode.tsx | 1 - .../DAO/loaders/governance/useERC721LinearStrategy.ts | 2 +- src/hooks/DAO/loaders/useFractalGovernance.ts | 7 +++---- src/hooks/DAO/loaders/useFractalGuardContracts.ts | 5 +++-- src/hooks/DAO/loaders/useFractalNode.ts | 2 +- src/hooks/utils/useSafeTransactions.ts | 1 - src/providers/App/guardContracts/action.ts | 2 +- src/providers/App/guardContracts/reducer.ts | 2 +- src/types/fractal.ts | 3 ++- 10 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/components/pages/DaoDashboard/Info/InfoGovernance.tsx b/src/components/pages/DaoDashboard/Info/InfoGovernance.tsx index 122f838270..a2362e67f4 100644 --- a/src/components/pages/DaoDashboard/Info/InfoGovernance.tsx +++ b/src/components/pages/DaoDashboard/Info/InfoGovernance.tsx @@ -33,7 +33,9 @@ export function InfoGovernance() { if (freezeGuardType == FreezeGuardType.MULTISIG) { if (freezeGuardContractAddress && baseContracts) { const freezeGuardContract = - baseContracts.multisigFreezeGuardMasterCopyContract.asProvider.attach(freezeGuardContractAddress); + baseContracts.multisigFreezeGuardMasterCopyContract.asProvider.attach( + freezeGuardContractAddress, + ); setTimelockPeriod(await formatBlocks(await freezeGuardContract.timelockPeriod())); setExecutionPeriod(await formatBlocks(await freezeGuardContract.executionPeriod())); } diff --git a/src/components/pages/DaoHierarchy/DaoNode.tsx b/src/components/pages/DaoHierarchy/DaoNode.tsx index 1011d11c47..0f2a4a490e 100644 --- a/src/components/pages/DaoHierarchy/DaoNode.tsx +++ b/src/components/pages/DaoHierarchy/DaoNode.tsx @@ -82,7 +82,6 @@ export function DaoNode({ } }, [loadDao, daoAddress, depth]); - return ( { const { governanceContracts: { erc721LinearVotingContractAddress, azoriusContractAddress }, - governance: {type}, + governance: { type }, action, } = useFractal(); const provider = useEthersProvider(); diff --git a/src/hooks/DAO/loaders/useFractalGovernance.ts b/src/hooks/DAO/loaders/useFractalGovernance.ts index 33e1bebaf1..7faea6934d 100644 --- a/src/hooks/DAO/loaders/useFractalGovernance.ts +++ b/src/hooks/DAO/loaders/useFractalGovernance.ts @@ -108,7 +108,6 @@ export const useFractalGovernance = () => { }); } } - }, [ governanceContracts, loadUnderlyingERC20Token, @@ -118,16 +117,16 @@ export const useFractalGovernance = () => { loadERC721Strategy, loadERC721Tokens, action, - type + type, ]); useEffect(() => { - const newLoadKey = (daoAddress || "0x"); + const newLoadKey = daoAddress || '0x'; if (type && daoAddress && daoAddress !== loadKey.current && isGuardLoaded) { loadKey.current = newLoadKey; loadDAOProposals(); } - if(!type || !daoAddress) { + if (!type || !daoAddress) { loadKey.current = undefined; } }, [type, loadDAOProposals, isGuardLoaded, daoAddress]); diff --git a/src/hooks/DAO/loaders/useFractalGuardContracts.ts b/src/hooks/DAO/loaders/useFractalGuardContracts.ts index dbffcef7bb..be37880f9b 100644 --- a/src/hooks/DAO/loaders/useFractalGuardContracts.ts +++ b/src/hooks/DAO/loaders/useFractalGuardContracts.ts @@ -22,7 +22,7 @@ export const useFractalGuardContracts = ({ loadOnMount = true }: { loadOnMount?: action, } = useFractal(); - const baseContracts = useSafeContracts() + const baseContracts = useSafeContracts(); const { chainId } = useNetworkConfig(); @@ -119,7 +119,8 @@ export const useFractalGuardContracts = ({ loadOnMount = true }: { loadOnMount?: loadOnMount && daoAddress && daoAddress + chainId !== loadKey.current && - isHierarchyLoaded && safe + isHierarchyLoaded && + safe ) { loadKey.current = daoAddress + chainId; setGuardContracts(); diff --git a/src/hooks/DAO/loaders/useFractalNode.ts b/src/hooks/DAO/loaders/useFractalNode.ts index 8e5afc7157..e2dc9a1d9c 100644 --- a/src/hooks/DAO/loaders/useFractalNode.ts +++ b/src/hooks/DAO/loaders/useFractalNode.ts @@ -132,7 +132,7 @@ export const useFractalNode = ({ daoAddress }: { daoAddress?: string }) => { if (daoAddress && chainId + daoAddress !== currentValidSafe.current) { setNodeLoading(true); setDAO(chainId, daoAddress); - currentValidSafe.current = chainId + daoAddress + currentValidSafe.current = chainId + daoAddress; } if (!daoAddress) { currentValidSafe.current = undefined; diff --git a/src/hooks/utils/useSafeTransactions.ts b/src/hooks/utils/useSafeTransactions.ts index 3d0bf3cd0c..52ff74d277 100644 --- a/src/hooks/utils/useSafeTransactions.ts +++ b/src/hooks/utils/useSafeTransactions.ts @@ -320,7 +320,6 @@ export const useSafeTransactions = () => { let freezeGuard: MultisigFreezeGuard | undefined; let freezeGuardData: FreezeGuardData | undefined; - if (guardContracts.freezeGuardContractAddress && baseContracts) { const blockNumber = await provider.getBlockNumber(); const averageBlockTime = BigNumber.from(Math.round(await getAverageBlockTime(provider))); diff --git a/src/providers/App/guardContracts/action.ts b/src/providers/App/guardContracts/action.ts index 3451394e20..8c990905a3 100644 --- a/src/providers/App/guardContracts/action.ts +++ b/src/providers/App/guardContracts/action.ts @@ -6,5 +6,5 @@ export enum GuardContractAction { export type GuardContractActions = { type: GuardContractAction.SET_GUARD_CONTRACT; - payload: Omit; + payload: Omit; }; diff --git a/src/providers/App/guardContracts/reducer.ts b/src/providers/App/guardContracts/reducer.ts index 21905c5501..6c28d6674c 100644 --- a/src/providers/App/guardContracts/reducer.ts +++ b/src/providers/App/guardContracts/reducer.ts @@ -14,7 +14,7 @@ export const guardContractReducer = ( ) => { switch (action.type) { case GuardContractAction.SET_GUARD_CONTRACT: - return {...action.payload, isGuardLoaded: true}; + return { ...action.payload, isGuardLoaded: true }; default: return state; } diff --git a/src/types/fractal.ts b/src/types/fractal.ts index 0b1cb964ed..94223dd398 100644 --- a/src/types/fractal.ts +++ b/src/types/fractal.ts @@ -243,7 +243,8 @@ export interface FractalNode { proposalTemplatesHash?: string; } -export interface Node extends Omit {} +export interface Node + extends Omit {} export interface FractalModuleData { moduleContract: Azorius | FractalModule | undefined; From 881c844875589e554a4c9440fc1830df95ea6074 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Sun, 24 Mar 2024 18:16:06 -0400 Subject: [PATCH 14/30] remove extra casting --- src/hooks/utils/useSafeTransactions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/utils/useSafeTransactions.ts b/src/hooks/utils/useSafeTransactions.ts index 52ff74d277..f495c075a6 100644 --- a/src/hooks/utils/useSafeTransactions.ts +++ b/src/hooks/utils/useSafeTransactions.ts @@ -330,8 +330,8 @@ export const useSafeTransactions = () => { const timelockPeriod = BigNumber.from(await freezeGuard.timelockPeriod()); const executionPeriod = BigNumber.from(await freezeGuard.executionPeriod()); freezeGuardData = { - guardTimelockPeriodMs: BigNumber.from(timelockPeriod.mul(averageBlockTime).mul(1000)), - guardExecutionPeriodMs: BigNumber.from(executionPeriod.mul(averageBlockTime).mul(1000)), + guardTimelockPeriodMs: timelockPeriod.mul(averageBlockTime).mul(1000), + guardExecutionPeriodMs: executionPeriod.mul(averageBlockTime).mul(1000), lastBlock: await provider.getBlock(blockNumber), }; } From 422484bf4fa560e2d37416593966a8032d336f74 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Mon, 25 Mar 2024 16:24:55 -0400 Subject: [PATCH 15/30] remove redundent func call --- src/hooks/DAO/loaders/useProposals.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hooks/DAO/loaders/useProposals.ts b/src/hooks/DAO/loaders/useProposals.ts index 482af484fd..7d487f7c94 100644 --- a/src/hooks/DAO/loaders/useProposals.ts +++ b/src/hooks/DAO/loaders/useProposals.ts @@ -18,6 +18,7 @@ export const useDAOProposals = () => { const loadSafeMultisigProposals = useSafeMultisigProposals(); const loadDAOProposals = useCallback(async () => { + clearIntervals(); if (type === GovernanceType.AZORIUS_ERC20 || type === GovernanceType.AZORIUS_ERC721) { // load Azorius proposals and strategies const proposals = await loadAzoriusProposals(); @@ -25,10 +26,8 @@ export const useDAOProposals = () => { type: FractalGovernanceAction.SET_PROPOSALS, payload: proposals, }); - clearIntervals(); } else if (type === GovernanceType.MULTISIG) { // load mulisig proposals - clearIntervals(); setMethodOnInterval(loadSafeMultisigProposals); } }, [ From fae4b78d3b8ffc0bf84b444e7dbbcb992f46cc93 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Mon, 25 Mar 2024 16:29:48 -0400 Subject: [PATCH 16/30] add missing attached addresses --- src/hooks/DAO/useCastFreezeVote.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/DAO/useCastFreezeVote.ts b/src/hooks/DAO/useCastFreezeVote.ts index 937e31e7c3..058b407cb3 100644 --- a/src/hooks/DAO/useCastFreezeVote.ts +++ b/src/hooks/DAO/useCastFreezeVote.ts @@ -33,7 +33,7 @@ const useCastFreezeVote = ({ contractFn: () => { if (freezeVotingType === FreezeVotingType.ERC721) { const freezeERC721VotingContract = - baseContracts.freezeERC721VotingMasterCopyContract.asSigner; + baseContracts.freezeERC721VotingMasterCopyContract.asSigner.attach(freezeVotingContractAddress); return getUserERC721VotingTokens(undefined, parentAddress).then(tokensInfo => freezeERC721VotingContract['castFreezeVote(address[],uint256[])']( tokensInfo.totalVotingTokenAddresses, @@ -42,7 +42,7 @@ const useCastFreezeVote = ({ ); } else { const freezeVotingContract = - baseContracts.freezeMultisigVotingMasterCopyContract.asSigner; + baseContracts.freezeMultisigVotingMasterCopyContract.asSigner.attach(freezeVotingContractAddress); return freezeVotingContract.castFreezeVote(); } }, From e2933f36c42bad5800eea268ea3d83242b40f66b Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Mon, 25 Mar 2024 16:33:38 -0400 Subject: [PATCH 17/30] fix missing attached address --- src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx index da5bb0a249..50801f833c 100644 --- a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx +++ b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx @@ -55,6 +55,7 @@ export function ManageDAOMenu({ const { node: { safe }, governance: { type }, + guardContracts: { freezeVotingContractAddress }, } = useFractal(); const baseContracts = useSafeContracts(); const currentTime = BigNumber.from(useBlockTimestamp()); @@ -139,8 +140,8 @@ export function ManageDAOMenu({ const freezeOption = { optionKey: 'optionInitiateFreeze', onClick: () => { - const freezeVotingContract = baseContracts?.freezeMultisigVotingMasterCopyContract.asSigner; - const freezeVotingType = guardContracts?.freezeVotingType; + const freezeVotingContract = baseContracts!.freezeMultisigVotingMasterCopyContract.asSigner.attach(freezeVotingContractAddress!); + const freezeVotingType = guardContracts!.freezeVotingType; if (freezeVotingContract) { if ( freezeVotingType === FreezeVotingType.MULTISIG || @@ -150,8 +151,8 @@ export function ManageDAOMenu({ } else if (freezeVotingType === FreezeVotingType.ERC721) { getUserERC721VotingTokens(undefined, parentAddress).then(tokensInfo => { const freezeERC721VotingContract = - baseContracts?.freezeERC721VotingMasterCopyContract.asSigner; - return freezeERC721VotingContract['castFreezeVote(address[],uint256[])']( + baseContracts!.freezeERC721VotingMasterCopyContract.asSigner.attach(freezeVotingContractAddress!); + return freezeERC721VotingContract!['castFreezeVote(address[],uint256[])']( tokensInfo.totalVotingTokenAddresses, tokensInfo.totalVotingTokenIds, ); @@ -236,6 +237,7 @@ export function ManageDAOMenu({ handleNavigateToSettings, getUserERC721VotingTokens, baseContracts, + freezeVotingContractAddress ]); return ( From 5b40d562723f7f3470990610fb4d02179d87197d Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Mon, 25 Mar 2024 16:44:24 -0400 Subject: [PATCH 18/30] remove unneeded dependencies --- src/components/ui/proposal/useProposalCountdown.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/ui/proposal/useProposalCountdown.tsx b/src/components/ui/proposal/useProposalCountdown.tsx index b4ae3d837c..b3d7e832e9 100644 --- a/src/components/ui/proposal/useProposalCountdown.tsx +++ b/src/components/ui/proposal/useProposalCountdown.tsx @@ -184,8 +184,7 @@ export function useProposalCountdown(proposal: FractalProposal) { return () => { clearInterval(countdownInterval.current); }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [proposal.state, azoriusGovernance.votingStrategy, freezeGuardType, baseContracts]); + }, [baseContracts, getCountdown]); return secondsLeft; } From 2b9533d6406e03e79405a52db1b0e166ac4b0e08 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Mon, 25 Mar 2024 16:45:05 -0400 Subject: [PATCH 19/30] run pretty/lint --- src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx | 11 ++++++++--- src/hooks/DAO/useCastFreezeVote.ts | 8 ++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx index 50801f833c..e8c5177dd1 100644 --- a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx +++ b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx @@ -140,7 +140,10 @@ export function ManageDAOMenu({ const freezeOption = { optionKey: 'optionInitiateFreeze', onClick: () => { - const freezeVotingContract = baseContracts!.freezeMultisigVotingMasterCopyContract.asSigner.attach(freezeVotingContractAddress!); + const freezeVotingContract = + baseContracts!.freezeMultisigVotingMasterCopyContract.asSigner.attach( + freezeVotingContractAddress!, + ); const freezeVotingType = guardContracts!.freezeVotingType; if (freezeVotingContract) { if ( @@ -151,7 +154,9 @@ export function ManageDAOMenu({ } else if (freezeVotingType === FreezeVotingType.ERC721) { getUserERC721VotingTokens(undefined, parentAddress).then(tokensInfo => { const freezeERC721VotingContract = - baseContracts!.freezeERC721VotingMasterCopyContract.asSigner.attach(freezeVotingContractAddress!); + baseContracts!.freezeERC721VotingMasterCopyContract.asSigner.attach( + freezeVotingContractAddress!, + ); return freezeERC721VotingContract!['castFreezeVote(address[],uint256[])']( tokensInfo.totalVotingTokenAddresses, tokensInfo.totalVotingTokenIds, @@ -237,7 +242,7 @@ export function ManageDAOMenu({ handleNavigateToSettings, getUserERC721VotingTokens, baseContracts, - freezeVotingContractAddress + freezeVotingContractAddress, ]); return ( diff --git a/src/hooks/DAO/useCastFreezeVote.ts b/src/hooks/DAO/useCastFreezeVote.ts index 058b407cb3..44658cdfe1 100644 --- a/src/hooks/DAO/useCastFreezeVote.ts +++ b/src/hooks/DAO/useCastFreezeVote.ts @@ -33,7 +33,9 @@ const useCastFreezeVote = ({ contractFn: () => { if (freezeVotingType === FreezeVotingType.ERC721) { const freezeERC721VotingContract = - baseContracts.freezeERC721VotingMasterCopyContract.asSigner.attach(freezeVotingContractAddress); + baseContracts.freezeERC721VotingMasterCopyContract.asSigner.attach( + freezeVotingContractAddress, + ); return getUserERC721VotingTokens(undefined, parentAddress).then(tokensInfo => freezeERC721VotingContract['castFreezeVote(address[],uint256[])']( tokensInfo.totalVotingTokenAddresses, @@ -42,7 +44,9 @@ const useCastFreezeVote = ({ ); } else { const freezeVotingContract = - baseContracts.freezeMultisigVotingMasterCopyContract.asSigner.attach(freezeVotingContractAddress); + baseContracts.freezeMultisigVotingMasterCopyContract.asSigner.attach( + freezeVotingContractAddress, + ); return freezeVotingContract.castFreezeVote(); } }, From abecdb9df54849240e06a75082da44241be829bc Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Mon, 25 Mar 2024 16:49:15 -0400 Subject: [PATCH 20/30] switch to asProvider --- src/hooks/DAO/loaders/useFractalFreeze.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hooks/DAO/loaders/useFractalFreeze.ts b/src/hooks/DAO/loaders/useFractalFreeze.ts index 0937f777fa..10f6abd90c 100644 --- a/src/hooks/DAO/loaders/useFractalFreeze.ts +++ b/src/hooks/DAO/loaders/useFractalFreeze.ts @@ -60,7 +60,7 @@ export const useFractalFreeze = ({ // @dev using freeze 'multisig' contract but these functions are the same for all freeze types const freezeVotingContract = - baseContracts.freezeMultisigVotingMasterCopyContract.asSigner.attach( + baseContracts.freezeMultisigVotingMasterCopyContract.asProvider.attach( freezeVotingContractAddress, ); let userHasVotes: boolean = false; @@ -106,7 +106,7 @@ export const useFractalFreeze = ({ const owners = await safeContract.getOwners(); userHasVotes = owners.find(owner => owner === account) !== undefined; } else if (freezeVotingType === FreezeVotingType.ERC20) { - const freezeERC20VotingContract = freezeERC20VotingMasterCopyContract.asSigner.attach( + const freezeERC20VotingContract = freezeERC20VotingMasterCopyContract.asProvider.attach( freezeVotingContractAddress, ); const votesTokenContract = votesTokenMasterCopyContract!.asProvider.attach( @@ -185,7 +185,7 @@ export const useFractalFreeze = ({ // @dev using freeze 'multisig' contract but these functions are the same for all freeze types let votingRPC: MultisigFreezeVoting | ERC20FreezeVoting | ERC721FreezeVoting = - freezeMultisigVotingMasterCopyContract.asSigner.attach(freezeVotingContractAddress); + freezeMultisigVotingMasterCopyContract.asProvider.attach(freezeVotingContractAddress); const listenerCallback: TypedListener = async ( voter: string, From 69ed790e3daea7626c1d8a7420d302a98e048ec5 Mon Sep 17 00:00:00 2001 From: Adam Gall Date: Mon, 25 Mar 2024 17:36:36 -0400 Subject: [PATCH 21/30] Nitpick: removed extra newline --- src/hooks/DAO/loaders/useFractalGuardContracts.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hooks/DAO/loaders/useFractalGuardContracts.ts b/src/hooks/DAO/loaders/useFractalGuardContracts.ts index be37880f9b..520e9f186c 100644 --- a/src/hooks/DAO/loaders/useFractalGuardContracts.ts +++ b/src/hooks/DAO/loaders/useFractalGuardContracts.ts @@ -19,7 +19,6 @@ export const useFractalGuardContracts = ({ loadOnMount = true }: { loadOnMount?: const loadKey = useRef(); const { node: { daoAddress, safe, fractalModules, isHierarchyLoaded }, - action, } = useFractal(); const baseContracts = useSafeContracts(); From 057f9110150fcc4fdaa1a9e6079482c5fc42cc72 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Mon, 25 Mar 2024 18:03:04 -0400 Subject: [PATCH 22/30] asSigner -> asProvider --- src/hooks/utils/useSafeTransactions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/utils/useSafeTransactions.ts b/src/hooks/utils/useSafeTransactions.ts index f495c075a6..770285063a 100644 --- a/src/hooks/utils/useSafeTransactions.ts +++ b/src/hooks/utils/useSafeTransactions.ts @@ -323,7 +323,7 @@ export const useSafeTransactions = () => { if (guardContracts.freezeGuardContractAddress && baseContracts) { const blockNumber = await provider.getBlockNumber(); const averageBlockTime = BigNumber.from(Math.round(await getAverageBlockTime(provider))); - freezeGuard = baseContracts.multisigFreezeGuardMasterCopyContract.asSigner.attach( + freezeGuard = baseContracts.multisigFreezeGuardMasterCopyContract.asProvider.attach( guardContracts.freezeGuardContractAddress, ); From 7b34ac6060f086eee17ff9b4d002aada78241ee4 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Mar 2024 23:44:22 -0400 Subject: [PATCH 23/30] reduce the number of requests by adding load key --- .../DAO/loaders/governance/useERC20Claim.ts | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/hooks/DAO/loaders/governance/useERC20Claim.ts b/src/hooks/DAO/loaders/governance/useERC20Claim.ts index c3ad1ff3a6..16032bcb62 100644 --- a/src/hooks/DAO/loaders/governance/useERC20Claim.ts +++ b/src/hooks/DAO/loaders/governance/useERC20Claim.ts @@ -1,4 +1,4 @@ -import { useEffect, useCallback } from 'react'; +import { useEffect, useCallback, useRef } from 'react'; import { useFractal } from '../../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; import useSafeContracts from '../../../safe/useSafeContracts'; @@ -15,13 +15,19 @@ export function useERC20Claim() { } = useFractal(); const baseContracts = useSafeContracts(); const loadTokenClaimContract = useCallback(async () => { - if (!baseContracts || !votesTokenContractAddress) return; + if (!baseContracts || !votesTokenContractAddress) { + return; + } const { claimingMasterCopyContract } = baseContracts; + const votesTokenContract = baseContracts.votesTokenMasterCopyContract.asProvider.attach(votesTokenContractAddress); + const approvalFilter = votesTokenContract.filters.Approval(); const approvals = await votesTokenContract.queryFilter(approvalFilter); - if (!approvals.length) return; + if (!approvals.length) { + return; + } const possibleTokenClaimContract = claimingMasterCopyContract.asProvider.attach( approvals[0].args[1], ); @@ -39,11 +45,20 @@ export function useERC20Claim() { payload: possibleTokenClaimContract, }); }, [baseContracts, votesTokenContractAddress, action]); + const loadKey = useRef(); useEffect(() => { - if (daoAddress) { + if ( + daoAddress && + votesTokenContractAddress && + daoAddress + votesTokenContractAddress !== loadKey.current + ) { + loadKey.current = daoAddress + votesTokenContractAddress; loadTokenClaimContract(); } - }, [loadTokenClaimContract, daoAddress]); + if (!daoAddress || !votesTokenContractAddress) { + loadKey.current = undefined; + } + }, [loadTokenClaimContract, daoAddress, votesTokenContractAddress]); return; } From 69cdc16b5efffef995fda9a17a86922d955ccba8 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:10:00 -0400 Subject: [PATCH 24/30] add check if DAOAddress has changed --- src/hooks/DAO/useDAOController.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/hooks/DAO/useDAOController.ts b/src/hooks/DAO/useDAOController.ts index 568f4e92b6..8864103263 100644 --- a/src/hooks/DAO/useDAOController.ts +++ b/src/hooks/DAO/useDAOController.ts @@ -1,4 +1,4 @@ -import { useEffect } from 'react'; +import { useEffect, useRef } from 'react'; import { useParams } from 'react-router-dom'; import { useFractal } from '../../providers/App/AppProvider'; import { useERC20Claim } from './loaders/governance/useERC20Claim'; @@ -11,6 +11,7 @@ import { useFractalTreasury } from './loaders/useFractalTreasury'; import { useGovernanceContracts } from './loaders/useGovernanceContracts'; export default function useDAOController() { + const currentDAOAddress = useRef(null); const { daoAddress } = useParams(); const { node: { @@ -19,12 +20,16 @@ export default function useDAOController() { action, } = useFractal(); useEffect(() => { - if (!daoAddress) { + if (!daoAddress || daoAddress !== currentDAOAddress.current) { action.resetDAO(); + currentDAOAddress.current = null; + } + if (daoAddress && !currentDAOAddress.current) { + currentDAOAddress.current = daoAddress; } }, [action, daoAddress]); - const { nodeLoading, errorLoading } = useFractalNode({ daoAddress }); + const { nodeLoading, errorLoading } = useFractalNode({ daoAddress: currentDAOAddress.current || '' }); useGovernanceContracts(); useFractalGuardContracts({}); useFractalFreeze({ parentSafeAddress: parentAddress }); From 09d0d8db872671e5f4b3c8f0fae33a6883d77bfc Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:35:10 -0400 Subject: [PATCH 25/30] reorder if statements; fix typing --- src/hooks/DAO/useDAOController.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hooks/DAO/useDAOController.ts b/src/hooks/DAO/useDAOController.ts index 8864103263..e32f61c932 100644 --- a/src/hooks/DAO/useDAOController.ts +++ b/src/hooks/DAO/useDAOController.ts @@ -11,7 +11,7 @@ import { useFractalTreasury } from './loaders/useFractalTreasury'; import { useGovernanceContracts } from './loaders/useGovernanceContracts'; export default function useDAOController() { - const currentDAOAddress = useRef(null); + const currentDAOAddress = useRef(); const { daoAddress } = useParams(); const { node: { @@ -20,16 +20,16 @@ export default function useDAOController() { action, } = useFractal(); useEffect(() => { - if (!daoAddress || daoAddress !== currentDAOAddress.current) { - action.resetDAO(); - currentDAOAddress.current = null; - } if (daoAddress && !currentDAOAddress.current) { currentDAOAddress.current = daoAddress; } + if (!daoAddress || daoAddress !== currentDAOAddress.current) { + action.resetDAO(); + currentDAOAddress.current = undefined; + } }, [action, daoAddress]); - const { nodeLoading, errorLoading } = useFractalNode({ daoAddress: currentDAOAddress.current || '' }); + const { nodeLoading, errorLoading } = useFractalNode({ daoAddress: currentDAOAddress.current }); useGovernanceContracts(); useFractalGuardContracts({}); useFractalFreeze({ parentSafeAddress: parentAddress }); From 4482e2dd37c66a1d2767713ee97c62c7135db8ca Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:59:48 -0400 Subject: [PATCH 26/30] initiate freeze from heirarchy; - move freezeOption to own memo - update logic to use prop guardcontract instead of global state --- .../ui/menus/ManageDAO/ManageDAOMenu.tsx | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx index e8c5177dd1..ec03a63fe9 100644 --- a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx +++ b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx @@ -55,7 +55,6 @@ export function ManageDAOMenu({ const { node: { safe }, governance: { type }, - guardContracts: { freezeVotingContractAddress }, } = useFractal(); const baseContracts = useSafeContracts(); const currentTime = BigNumber.from(useBlockTimestamp()); @@ -131,18 +130,13 @@ export function ManageDAOMenu({ const handleModifyGovernance = useFractalModal(ModalType.CONFIRM_MODIFY_GOVERNANCE); - const options = useMemo(() => { - const createSubDAOOption = { - optionKey: 'optionCreateSubDAO', - onClick: () => navigate(DAO_ROUTES.newSubDao.relative(safeAddress), { replace: true }), - }; - - const freezeOption = { + const freezeOption = useMemo( + () => ({ optionKey: 'optionInitiateFreeze', onClick: () => { const freezeVotingContract = baseContracts!.freezeMultisigVotingMasterCopyContract.asSigner.attach( - freezeVotingContractAddress!, + guardContracts!.freezeVotingContractAddress!, ); const freezeVotingType = guardContracts!.freezeVotingType; if (freezeVotingContract) { @@ -150,12 +144,14 @@ export function ManageDAOMenu({ freezeVotingType === FreezeVotingType.MULTISIG || freezeVotingType === FreezeVotingType.ERC20 ) { - (freezeVotingContract as ERC20FreezeVoting | MultisigFreezeVoting).castFreezeVote(); + return ( + freezeVotingContract as ERC20FreezeVoting | MultisigFreezeVoting + ).castFreezeVote(); } else if (freezeVotingType === FreezeVotingType.ERC721) { getUserERC721VotingTokens(undefined, parentAddress).then(tokensInfo => { const freezeERC721VotingContract = baseContracts!.freezeERC721VotingMasterCopyContract.asSigner.attach( - freezeVotingContractAddress!, + guardContracts!.freezeVotingContractAddress!, ); return freezeERC721VotingContract!['castFreezeVote(address[],uint256[])']( tokensInfo.totalVotingTokenAddresses, @@ -165,8 +161,20 @@ export function ManageDAOMenu({ } } }, - }; + }), + [ + baseContracts, + guardContracts, + getUserERC721VotingTokens, + parentAddress, + ], + ); + const options = useMemo(() => { + const createSubDAOOption = { + optionKey: 'optionCreateSubDAO', + onClick: () => navigate(DAO_ROUTES.newSubDao.relative(safeAddress), { replace: true }), + }; const clawBackOption = { optionKey: 'optionInitiateClawback', onClick: handleClawBack, @@ -233,16 +241,12 @@ export function ManageDAOMenu({ currentTime, navigate, safeAddress, - parentAddress, governanceType, - guardContracts, handleClawBack, canUserCreateProposal, handleModifyGovernance, handleNavigateToSettings, - getUserERC721VotingTokens, - baseContracts, - freezeVotingContractAddress, + freezeOption, ]); return ( From 90a8798b059d7a6a63c9c0ca8dd253001ef8c7e2 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Wed, 27 Mar 2024 14:00:51 -0400 Subject: [PATCH 27/30] run pretty/lint --- src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx index ec03a63fe9..7dfd7e6186 100644 --- a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx +++ b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx @@ -162,12 +162,7 @@ export function ManageDAOMenu({ } }, }), - [ - baseContracts, - guardContracts, - getUserERC721VotingTokens, - parentAddress, - ], + [baseContracts, guardContracts, getUserERC721VotingTokens, parentAddress], ); const options = useMemo(() => { From 678d48d9da1cdee4fbdbd1fd10cce14217b7e1ef Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Wed, 27 Mar 2024 22:25:09 -0400 Subject: [PATCH 28/30] Fix DAO state not reseting when switching DAOs --- src/hooks/DAO/useDAOController.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/hooks/DAO/useDAOController.ts b/src/hooks/DAO/useDAOController.ts index 48161072a6..649bb2c6a1 100644 --- a/src/hooks/DAO/useDAOController.ts +++ b/src/hooks/DAO/useDAOController.ts @@ -22,10 +22,11 @@ export default function useDAOController() { } = useFractal(); useEffect(() => { if (daoAddress && !currentDAOAddress.current) { - currentDAOAddress.current = daoAddress; + action.resetDAO().then(() => { + currentDAOAddress.current = daoAddress; + }) } if (!daoAddress || daoAddress !== currentDAOAddress.current) { - action.resetDAO(); currentDAOAddress.current = undefined; } }, [action, daoAddress]); From c3ce72444b51c0fd1c68540e378a22b4eaa4e394 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Wed, 27 Mar 2024 22:28:29 -0400 Subject: [PATCH 29/30] fix freeze showing up when switching DAOs. --- src/components/pages/DaoDashboard/Activities/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/pages/DaoDashboard/Activities/index.tsx b/src/components/pages/DaoDashboard/Activities/index.tsx index aecd7dd7f8..009e63add1 100644 --- a/src/components/pages/DaoDashboard/Activities/index.tsx +++ b/src/components/pages/DaoDashboard/Activities/index.tsx @@ -14,6 +14,7 @@ import { useActivities } from './hooks/useActivities'; export function Activities() { const { + guardContracts: { freezeVotingContractAddress }, guard, governance: { type }, } = useFractal(); @@ -37,7 +38,7 @@ export function Activities() { flexDirection="column" gap="1rem" > - {guard.freezeProposalVoteCount?.gt(0) && } + {freezeVotingContractAddress && guard.freezeProposalVoteCount?.gt(0) && } {!type ? ( ) : sortedActivities.length ? ( From a48370eb4bf9df2b0580042d778e703e20804a87 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Wed, 27 Mar 2024 22:28:44 -0400 Subject: [PATCH 30/30] run pretty/lint --- src/hooks/DAO/useDAOController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/DAO/useDAOController.ts b/src/hooks/DAO/useDAOController.ts index 649bb2c6a1..f769dc91c1 100644 --- a/src/hooks/DAO/useDAOController.ts +++ b/src/hooks/DAO/useDAOController.ts @@ -24,7 +24,7 @@ export default function useDAOController() { if (daoAddress && !currentDAOAddress.current) { action.resetDAO().then(() => { currentDAOAddress.current = daoAddress; - }) + }); } if (!daoAddress || daoAddress !== currentDAOAddress.current) { currentDAOAddress.current = undefined;