diff --git a/src/assets/abi/Azorius.ts b/src/assets/abi/Azorius.ts new file mode 100644 index 0000000000..0ba697132c --- /dev/null +++ b/src/assets/abi/Azorius.ts @@ -0,0 +1,878 @@ +const AzoriusAbi = [ + { + inputs: [], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + inputs: [], + name: 'InvalidArrayLengths', + type: 'error', + }, + { + inputs: [], + name: 'InvalidProposal', + type: 'error', + }, + { + inputs: [], + name: 'InvalidProposer', + type: 'error', + }, + { + inputs: [], + name: 'InvalidStrategy', + type: 'error', + }, + { + inputs: [], + name: 'InvalidTxHash', + type: 'error', + }, + { + inputs: [], + name: 'InvalidTxs', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'guard_', + type: 'address', + }, + ], + name: 'NotIERC165Compliant', + type: 'error', + }, + { + inputs: [], + name: 'ProposalNotExecutable', + type: 'error', + }, + { + inputs: [], + name: 'StrategyDisabled', + type: 'error', + }, + { + inputs: [], + name: 'StrategyEnabled', + type: 'error', + }, + { + inputs: [], + name: 'TxFailed', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'previousAvatar', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'newAvatar', + type: 'address', + }, + ], + name: 'AvatarSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'creator', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'avatar', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'target', + type: 'address', + }, + ], + name: 'AzoriusSetUp', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'guard', + type: 'address', + }, + ], + name: 'ChangedGuard', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'strategy', + type: 'address', + }, + ], + name: 'DisabledStrategy', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'strategy', + type: 'address', + }, + ], + name: 'EnabledStrategy', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint32', + name: 'executionPeriod', + type: 'uint32', + }, + ], + name: 'ExecutionPeriodUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint8', + name: 'version', + type: 'uint8', + }, + ], + name: 'Initialized', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'previousOwner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'OwnershipTransferred', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'strategy', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'proposalId', + type: 'uint256', + }, + { + indexed: false, + internalType: 'address', + name: 'proposer', + type: 'address', + }, + { + components: [ + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + { + internalType: 'bytes', + name: 'data', + type: 'bytes', + }, + { + internalType: 'enum Enum.Operation', + name: 'operation', + type: 'uint8', + }, + ], + indexed: false, + internalType: 'struct IAzorius.Transaction[]', + name: 'transactions', + type: 'tuple[]', + }, + { + indexed: false, + internalType: 'string', + name: 'metadata', + type: 'string', + }, + ], + name: 'ProposalCreated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint32', + name: 'proposalId', + type: 'uint32', + }, + { + indexed: false, + internalType: 'bytes32[]', + name: 'txHashes', + type: 'bytes32[]', + }, + ], + name: 'ProposalExecuted', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'previousTarget', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'newTarget', + type: 'address', + }, + ], + name: 'TargetSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint32', + name: 'timelockPeriod', + type: 'uint32', + }, + ], + name: 'TimelockPeriodUpdated', + type: 'event', + }, + { + inputs: [], + name: 'DOMAIN_SEPARATOR_TYPEHASH', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'TRANSACTION_TYPEHASH', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'avatar', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_prevStrategy', + type: 'address', + }, + { + internalType: 'address', + name: '_strategy', + type: 'address', + }, + ], + name: 'disableStrategy', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_strategy', + type: 'address', + }, + ], + name: 'enableStrategy', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint32', + name: '_proposalId', + type: 'uint32', + }, + { + internalType: 'address[]', + name: '_targets', + type: 'address[]', + }, + { + internalType: 'uint256[]', + name: '_values', + type: 'uint256[]', + }, + { + internalType: 'bytes[]', + name: '_data', + type: 'bytes[]', + }, + { + internalType: 'enum Enum.Operation[]', + name: '_operations', + type: 'uint8[]', + }, + ], + name: 'executeProposal', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'executionPeriod', + outputs: [ + { + internalType: 'uint32', + name: '', + type: 'uint32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_to', + type: 'address', + }, + { + internalType: 'uint256', + name: '_value', + type: 'uint256', + }, + { + internalType: 'bytes', + name: '_data', + type: 'bytes', + }, + { + internalType: 'enum Enum.Operation', + name: '_operation', + type: 'uint8', + }, + { + internalType: 'uint256', + name: '_nonce', + type: 'uint256', + }, + ], + name: 'generateTxHashData', + outputs: [ + { + internalType: 'bytes', + name: '', + type: 'bytes', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getGuard', + outputs: [ + { + internalType: 'address', + name: '_guard', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint32', + name: '_proposalId', + type: 'uint32', + }, + ], + name: 'getProposal', + outputs: [ + { + internalType: 'address', + name: '_strategy', + type: 'address', + }, + { + internalType: 'bytes32[]', + name: '_txHashes', + type: 'bytes32[]', + }, + { + internalType: 'uint32', + name: '_timelockPeriod', + type: 'uint32', + }, + { + internalType: 'uint32', + name: '_executionPeriod', + type: 'uint32', + }, + { + internalType: 'uint32', + name: '_executionCounter', + type: 'uint32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint32', + name: '_proposalId', + type: 'uint32', + }, + { + internalType: 'uint32', + name: '_txIndex', + type: 'uint32', + }, + ], + name: 'getProposalTxHash', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint32', + name: '_proposalId', + type: 'uint32', + }, + ], + name: 'getProposalTxHashes', + outputs: [ + { + internalType: 'bytes32[]', + name: '', + type: 'bytes32[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_startAddress', + type: 'address', + }, + { + internalType: 'uint256', + name: '_count', + type: 'uint256', + }, + ], + name: 'getStrategies', + outputs: [ + { + internalType: 'address[]', + name: '_strategies', + type: 'address[]', + }, + { + internalType: 'address', + name: '_next', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_to', + type: 'address', + }, + { + internalType: 'uint256', + name: '_value', + type: 'uint256', + }, + { + internalType: 'bytes', + name: '_data', + type: 'bytes', + }, + { + internalType: 'enum Enum.Operation', + name: '_operation', + type: 'uint8', + }, + ], + name: 'getTxHash', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'guard', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_strategy', + type: 'address', + }, + ], + name: 'isStrategyEnabled', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'owner', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint32', + name: '_proposalId', + type: 'uint32', + }, + ], + name: 'proposalState', + outputs: [ + { + internalType: 'enum IAzorius.ProposalState', + name: '', + type: 'uint8', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'renounceOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_avatar', + type: 'address', + }, + ], + name: 'setAvatar', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_guard', + type: 'address', + }, + ], + name: 'setGuard', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_target', + type: 'address', + }, + ], + name: 'setTarget', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes', + name: 'initializeParams', + type: 'bytes', + }, + ], + name: 'setUp', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_strategy', + type: 'address', + }, + { + internalType: 'bytes', + name: '_data', + type: 'bytes', + }, + { + components: [ + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + { + internalType: 'bytes', + name: 'data', + type: 'bytes', + }, + { + internalType: 'enum Enum.Operation', + name: 'operation', + type: 'uint8', + }, + ], + internalType: 'struct IAzorius.Transaction[]', + name: '_transactions', + type: 'tuple[]', + }, + { + internalType: 'string', + name: '_metadata', + type: 'string', + }, + ], + name: 'submitProposal', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'target', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'timelockPeriod', + outputs: [ + { + internalType: 'uint32', + name: '', + type: 'uint32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalProposalCount', + outputs: [ + { + internalType: 'uint32', + name: '', + type: 'uint32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'transferOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint32', + name: '_executionPeriod', + type: 'uint32', + }, + ], + name: 'updateExecutionPeriod', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint32', + name: '_timelockPeriod', + type: 'uint32', + }, + ], + name: 'updateTimelockPeriod', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const; + +export default AzoriusAbi; diff --git a/src/components/pages/DaoHierarchy/useFetchNodes.tsx b/src/components/pages/DaoHierarchy/useFetchNodes.tsx index ef4ad4d74b..eaffa7a439 100644 --- a/src/components/pages/DaoHierarchy/useFetchNodes.tsx +++ b/src/components/pages/DaoHierarchy/useFetchNodes.tsx @@ -1,7 +1,9 @@ import { useQuery } from '@apollo/client'; import { useCallback, useEffect, useState } from 'react'; -import { getAddress, zeroAddress } from 'viem'; +import { getAddress, getContract, zeroAddress } from 'viem'; +import { usePublicClient } from 'wagmi'; import { DAOQueryDocument } from '../../../../.graphclient'; +import AzoriusAbi from '../../../assets/abi/Azorius'; import { logError } from '../../../helpers/errorLogging'; import { useFractalModules } from '../../../hooks/DAO/loaders/useFractalModules'; import { useAsyncRetry } from '../../../hooks/utils/useAsyncRetry'; @@ -23,6 +25,7 @@ export function useFetchNodes(address?: string) { const { requestWithRetries } = useAsyncRetry(); const { subgraph } = useNetworkConfig(); + const publicClient = usePublicClient(); const { data, error } = useQuery(DAOQueryDocument, { variables: { daoAddress: address }, skip: address === safe?.address || !address, // If address === safe.address - we already have hierarchy obtained in the context @@ -38,11 +41,8 @@ export function useFetchNodes(address?: string) { const getDAOOwner = useCallback( async (safeInfo?: Partial) => { if (safeInfo && safeInfo.guard && baseContracts) { - const { - multisigFreezeGuardMasterCopyContract, - azoriusFreezeGuardMasterCopyContract, - fractalAzoriusMasterCopyContract, - } = baseContracts; + const { multisigFreezeGuardMasterCopyContract, azoriusFreezeGuardMasterCopyContract } = + baseContracts; if (safeInfo.guard !== zeroAddress) { const guard = multisigFreezeGuardMasterCopyContract.asProvider.attach(safeInfo.guard); const guardOwner = await guard.owner(); @@ -53,15 +53,13 @@ export function useFetchNodes(address?: string) { const modules = await lookupModules(safeInfo.modules || []); if (!modules) return; const azoriusModule = getAzoriusModuleFromModules(modules); - if ( - azoriusModule && - azoriusFreezeGuardMasterCopyContract && - fractalAzoriusMasterCopyContract - ) { - const azoriusContract = fractalAzoriusMasterCopyContract?.asProvider.attach( - azoriusModule.moduleAddress, - ); - const azoriusGuardAddress = await azoriusContract.getGuard(); + if (azoriusModule && azoriusFreezeGuardMasterCopyContract && publicClient) { + const azoriusContract = getContract({ + abi: AzoriusAbi, + address: getAddress(azoriusModule.moduleAddress), + client: publicClient, + }); + const azoriusGuardAddress = await azoriusContract.read.getGuard(); if (azoriusGuardAddress !== zeroAddress) { const guard = azoriusFreezeGuardMasterCopyContract.asProvider.attach(azoriusGuardAddress); @@ -75,7 +73,7 @@ export function useFetchNodes(address?: string) { } return undefined; }, - [baseContracts, lookupModules], + [baseContracts, lookupModules, publicClient], ); const fetchDAOInfo = useCallback( diff --git a/src/components/ui/cards/DAOInfoCard.tsx b/src/components/ui/cards/DAOInfoCard.tsx index 2cdab4b0d6..30a989cd1c 100644 --- a/src/components/ui/cards/DAOInfoCard.tsx +++ b/src/components/ui/cards/DAOInfoCard.tsx @@ -86,7 +86,6 @@ export function DAOInfoCard() { {!!user.address && ( diff --git a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx index 2e22d7ac93..203c49a3c4 100644 --- a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx +++ b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx @@ -3,8 +3,7 @@ import { ERC20FreezeVoting, MultisigFreezeVoting } from '@fractal-framework/frac import { GearFine } from '@phosphor-icons/react'; import { useMemo, useCallback, useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import { Address, getAddress } from 'viem'; -import { SENTINEL_ADDRESS } from '../../../../constants/common'; +import { Address } from 'viem'; import { DAO_ROUTES } from '../../../../constants/routes'; import { isWithinFreezePeriod, @@ -16,23 +15,21 @@ import useSafeContracts from '../../../../hooks/safe/useSafeContracts'; import useBlockTimestamp from '../../../../hooks/utils/useBlockTimestamp'; import { useCanUserCreateProposal } from '../../../../hooks/utils/useCanUserSubmitProposal'; import { useMasterCopy } from '../../../../hooks/utils/useMasterCopy'; +import useVotingStrategyAddress from '../../../../hooks/utils/useVotingStrategyAddress'; import { useFractal } from '../../../../providers/App/AppProvider'; import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider'; import { FractalGuardContracts, - FractalNode, FreezeGuard, GovernanceType, FreezeVotingType, } from '../../../../types'; -import { getAzoriusModuleFromModules } from '../../../../utils'; import { ModalType } from '../../modals/ModalProvider'; import { useFractalModal } from '../../modals/useFractalModal'; import { OptionMenu } from '../OptionMenu'; interface IManageDAOMenu { parentAddress: Address | null; - fractalNode: FractalNode; freezeGuard: FreezeGuard; guardContracts: FractalGuardContracts; } @@ -46,57 +43,37 @@ interface IManageDAOMenu { * * All info for this menu should be supplied in the constructor. */ -export function ManageDAOMenu({ - parentAddress, - freezeGuard, - guardContracts, - fractalNode, -}: IManageDAOMenu) { +export function ManageDAOMenu({ parentAddress, freezeGuard, guardContracts }: IManageDAOMenu) { const [governanceType, setGovernanceType] = useState(GovernanceType.MULTISIG); const { - node: { safe }, + node, governance: { type }, } = useFractal(); const baseContracts = useSafeContracts(); const currentTime = BigInt(useBlockTimestamp()); const navigate = useNavigate(); - const safeAddress = fractalNode.daoAddress; + const safeAddress = node.daoAddress; const { getZodiacModuleProxyMasterCopyData } = useMasterCopy(); const { canUserCreateProposal } = useCanUserCreateProposal(); const { getUserERC721VotingTokens } = useUserERC721VotingTokens(safeAddress, undefined, false); const { handleClawBack } = useClawBack({ parentAddress, - childSafeInfo: fractalNode, + childSafeInfo: node, }); + const { getVotingStrategyAddress } = useVotingStrategyAddress(); useEffect(() => { const loadGovernanceType = async () => { - if (safe && safe.address && safe.address === safeAddress && type) { + if (node.safe && node.safe.address && node.safe.address === safeAddress && type) { // Since safe.address (global scope DAO address) and safeAddress(Node provided to this component via props) // are the same - we can simply grab governance type from global scope and avoid double-fetching setGovernanceType(type); } else { - if (baseContracts) { + if (node?.fractalModules) { let result = GovernanceType.MULTISIG; - const azoriusModule = getAzoriusModuleFromModules(fractalNode.fractalModules); - const { fractalAzoriusMasterCopyContract } = baseContracts; - if (!!azoriusModule) { - const azoriusContract = { - asProvider: fractalAzoriusMasterCopyContract.asProvider.attach( - azoriusModule.moduleAddress, - ), - asSigner: fractalAzoriusMasterCopyContract.asSigner.attach( - azoriusModule.moduleAddress, - ), - }; - - // @dev assumes the first strategy is the voting contract - const votingContractAddress = ( - await azoriusContract.asProvider.getStrategies(SENTINEL_ADDRESS, 0) - )[1]; - const masterCopyData = await getZodiacModuleProxyMasterCopyData( - getAddress(votingContractAddress), - ); + const votingContractAddress = await getVotingStrategyAddress(); + if (votingContractAddress) { + const masterCopyData = await getZodiacModuleProxyMasterCopyData(votingContractAddress); if (masterCopyData.isOzLinearVoting) { result = GovernanceType.AZORIUS_ERC20; @@ -111,7 +88,14 @@ export function ManageDAOMenu({ }; loadGovernanceType(); - }, [fractalNode, safe, safeAddress, type, getZodiacModuleProxyMasterCopyData, baseContracts]); + }, [ + getVotingStrategyAddress, + getZodiacModuleProxyMasterCopyData, + node?.fractalModules, + node.safe, + safeAddress, + type, + ]); const { addressPrefix } = useNetworkConfig(); const handleNavigateToSettings = useCallback(() => { diff --git a/src/components/ui/proposal/useProposalCountdown.tsx b/src/components/ui/proposal/useProposalCountdown.tsx index 92e85ec5dc..706ee8d308 100644 --- a/src/components/ui/proposal/useProposalCountdown.tsx +++ b/src/components/ui/proposal/useProposalCountdown.tsx @@ -62,7 +62,7 @@ export function useProposalCountdown(proposal: FractalProposal) { (async () => { try { if (dao?.isAzorius) { - await updateProposalState(BigInt(proposal.proposalId)); + await updateProposalState(Number(proposal.proposalId)); } else { await loadDAOProposals(); } diff --git a/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts b/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts index 95afa5b4ea..c46504100e 100644 --- a/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts +++ b/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts @@ -1,97 +1,21 @@ -import { TypedListener } from '@fractal-framework/fractal-contracts/dist/typechain-types/common'; -import { TimelockPeriodUpdatedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/MultisigFreezeGuard'; -import { - Azorius, - ProposalCreatedEvent, -} from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/Azorius'; -import { Dispatch, useEffect, useMemo } from 'react'; -import { getAddress, getContract, GetContractReturnType, Hex, PublicClient } from 'viem'; +import { useEffect, useMemo } from 'react'; +import { getAddress, getContract } from 'viem'; import { usePublicClient } from 'wagmi'; +import AzoriusAbi from '../../../../assets/abi/Azorius'; import LinearERC20VotingAbi from '../../../../assets/abi/LinearERC20Voting'; import LinearERC721VotingAbi from '../../../../assets/abi/LinearERC721Voting'; import { useFractal } from '../../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; import { useEthersProvider } from '../../../../providers/Ethers/hooks/useEthersProvider'; -import { - CreateProposalMetadata, - VotingStrategyType, - DecodedTransaction, - FractalActions, -} from '../../../../types'; -import { Providers } from '../../../../types/network'; +import { CreateProposalMetadata, VotingStrategyType } from '../../../../types'; import { getProposalVotesSummary, mapProposalCreatedEventToProposal, decodeTransactions, } from '../../../../utils'; import { getAverageBlockTime } from '../../../../utils/contract'; -import useSafeContracts from '../../../safe/useSafeContracts'; import { useSafeDecoder } from '../../../utils/useSafeDecoder'; -const proposalCreatedEventListener = ( - azoriusContract: Azorius, - erc20StrategyContract: - | GetContractReturnType - | undefined, - erc721StrategyContract: - | GetContractReturnType - | undefined, - provider: Providers, - strategyType: VotingStrategyType, - decode: (value: string, to: string, data?: string | undefined) => Promise, - dispatch: Dispatch, -): TypedListener => { - return async (_strategyAddress, proposalId, proposer, transactions, metadata) => { - // Wait for a block before processing. - // We've seen that calling smart contract functions in `mapProposalCreatedEventToProposal` - // which include the `proposalId` error out because the RPC node (rather, the block it's on) - // doesn't see this proposal yet (despite the event being caught in the app...). - const averageBlockTime = await getAverageBlockTime(provider); - await new Promise(resolve => setTimeout(resolve, averageBlockTime * 1000)); - - if (!metadata) { - return; - } - - const typedTransactions = transactions.map(t => ({ - ...t, - to: getAddress(t.to), - data: t.data as Hex, // @todo - this type casting shouldn't be needed after migrating to getContract - value: t.value.toBigInt(), - })); - - const metaDataEvent: CreateProposalMetadata = JSON.parse(metadata); - const proposalData = { - metaData: { - title: metaDataEvent.title, - description: metaDataEvent.description, - documentationUrl: metaDataEvent.documentationUrl, - }, - transactions: typedTransactions, - decodedTransactions: await decodeTransactions(decode, typedTransactions), - }; - - const proposal = await mapProposalCreatedEventToProposal( - erc20StrategyContract, - erc721StrategyContract, - strategyType, - proposalId.toBigInt(), - proposer, - azoriusContract, - provider, - Promise.resolve(undefined), - Promise.resolve(undefined), - Promise.resolve(undefined), - proposalData, - ); - - dispatch({ - type: FractalGovernanceAction.UPDATE_PROPOSALS_NEW, - payload: proposal, - }); - }; -}; - export const useAzoriusListeners = () => { const { action, @@ -102,19 +26,22 @@ export const useAzoriusListeners = () => { }, } = useFractal(); - const baseContracts = useSafeContracts(); const provider = useEthersProvider(); const decode = useSafeDecoder(); const publicClient = usePublicClient(); const azoriusContract = useMemo(() => { - if (!baseContracts || !azoriusContractAddress) { + if (!publicClient || !azoriusContractAddress) { return; } - return baseContracts.fractalAzoriusMasterCopyContract.asProvider.attach(azoriusContractAddress); - }, [azoriusContractAddress, baseContracts]); + return getContract({ + abi: AzoriusAbi, + address: getAddress(azoriusContractAddress), + client: publicClient, + }); + }, [azoriusContractAddress, publicClient]); const strategyType = useMemo(() => { if (ozLinearVotingContractAddress) { @@ -155,24 +82,71 @@ export const useAzoriusListeners = () => { return; } - const proposalCreatedFilter = azoriusContract.filters.ProposalCreated(); - const listener = proposalCreatedEventListener( - azoriusContract, - erc20StrategyContract, - erc721StrategyContract, - provider, - strategyType, - decode, - action.dispatch, - ); + const unwatch = azoriusContract.watchEvent.ProposalCreated({ + onLogs: async logs => { + for (const log of logs) { + if ( + !log.args.strategy || + !log.args.proposalId || + !log.args.metadata || + !log.args.transactions || + !log.args.proposer + ) { + continue; + } + + // Wait for a block before processing. + // We've seen that calling smart contract functions in `mapProposalCreatedEventToProposal` + // which include the `proposalId` error out because the RPC node (rather, the block it's on) + // doesn't see this proposal yet (despite the event being caught in the app...). + const averageBlockTime = await getAverageBlockTime(provider); + await new Promise(resolve => setTimeout(resolve, averageBlockTime * 1000)); + + const typedTransactions = log.args.transactions.map(t => ({ + ...t, + to: t.to, + data: t.data, + value: t.value, + })); + + const metaDataEvent: CreateProposalMetadata = JSON.parse(log.args.metadata); + const proposalData = { + metaData: { + title: metaDataEvent.title, + description: metaDataEvent.description, + documentationUrl: metaDataEvent.documentationUrl, + }, + transactions: typedTransactions, + decodedTransactions: await decodeTransactions(decode, typedTransactions), + }; + + const proposal = await mapProposalCreatedEventToProposal( + erc20StrategyContract, + erc721StrategyContract, + strategyType, + Number(log.args.proposalId), + log.args.proposer, + azoriusContract, + provider, + undefined, + undefined, + undefined, + proposalData, + ); - azoriusContract.on(proposalCreatedFilter, listener); + action.dispatch({ + type: FractalGovernanceAction.UPDATE_PROPOSALS_NEW, + payload: proposal, + }); + } + }, + }); return () => { - azoriusContract.off(proposalCreatedFilter, listener); + unwatch(); }; }, [ - action.dispatch, + action, azoriusContract, decode, erc20StrategyContract, @@ -197,7 +171,7 @@ export const useAzoriusListeners = () => { erc20StrategyContract, undefined, strategyType, - BigInt(log.args.proposalId), + log.args.proposalId, ); action.dispatch({ @@ -241,7 +215,7 @@ export const useAzoriusListeners = () => { undefined, erc721StrategyContract, strategyType, - BigInt(log.args.proposalId), + log.args.proposalId, ); action.dispatch({ @@ -269,18 +243,23 @@ export const useAzoriusListeners = () => { return; } - const timeLockPeriodFilter = azoriusContract.filters.TimelockPeriodUpdated(); - const timelockPeriodListener: TypedListener = timelockPeriod => { - action.dispatch({ - type: FractalGovernanceAction.UPDATE_TIMELOCK_PERIOD, - payload: BigInt(timelockPeriod), - }); - }; + const unwatch = azoriusContract.watchEvent.TimelockPeriodUpdated({ + onLogs: logs => { + for (const log of logs) { + if (!log.args.timelockPeriod) { + continue; + } - azoriusContract.on(timeLockPeriodFilter, timelockPeriodListener); + action.dispatch({ + type: FractalGovernanceAction.UPDATE_TIMELOCK_PERIOD, + payload: BigInt(log.args.timelockPeriod), + }); + } + }, + }); return () => { - azoriusContract.off(timeLockPeriodFilter, timelockPeriodListener); + unwatch(); }; }, [action, azoriusContract]); }; diff --git a/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts b/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts index 73cec33533..ce15715591 100644 --- a/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts +++ b/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts @@ -1,27 +1,27 @@ -import { - Azorius, - ProposalExecutedEvent, -} from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/Azorius'; import { useCallback, useEffect, useMemo, useRef } from 'react'; import { GetContractEventsReturnType, GetContractReturnType, - Hex, PublicClient, getAddress, getContract, } from 'viem'; import { usePublicClient } from 'wagmi'; +import AzoriusAbi from '../../../../assets/abi/Azorius'; import LinearERC20VotingAbi from '../../../../assets/abi/LinearERC20Voting'; import LinearERC721VotingAbi from '../../../../assets/abi/LinearERC721Voting'; import { logError } from '../../../../helpers/errorLogging'; import { useFractal } from '../../../../providers/App/AppProvider'; import { useEthersProvider } from '../../../../providers/Ethers/hooks/useEthersProvider'; -import { CreateProposalMetadata, VotingStrategyType, DecodedTransaction } from '../../../../types'; +import { + CreateProposalMetadata, + VotingStrategyType, + DecodedTransaction, + MetaTransaction, +} from '../../../../types'; import { AzoriusProposal } from '../../../../types/daoProposal'; import { Providers } from '../../../../types/network'; import { mapProposalCreatedEventToProposal, decodeTransactions } from '../../../../utils'; -import useSafeContracts from '../../../safe/useSafeContracts'; import { useSafeDecoder } from '../../../utils/useSafeDecoder'; export const useAzoriusProposals = () => { @@ -35,19 +35,22 @@ export const useAzoriusProposals = () => { }, } = useFractal(); - const baseContracts = useSafeContracts(); const provider = useEthersProvider(); const decode = useSafeDecoder(); const publicClient = usePublicClient(); const azoriusContract = useMemo(() => { - if (!baseContracts || !azoriusContractAddress) { + if (!azoriusContractAddress || !publicClient) { return; } - return baseContracts.fractalAzoriusMasterCopyContract.asProvider.attach(azoriusContractAddress); - }, [azoriusContractAddress, baseContracts]); + return getContract({ + abi: AzoriusAbi, + address: getAddress(azoriusContractAddress), + client: publicClient, + }); + }, [azoriusContractAddress, publicClient]); const strategyType = useMemo(() => { if (ozLinearVotingContractAddress) { @@ -88,7 +91,8 @@ export const useAzoriusProposals = () => { return; } - return erc20StrategyContract.getEvents.Voted(); + const events = await erc20StrategyContract.getEvents.Voted(); + return events; }, [erc20StrategyContract]); const erc721VotedEvents = useMemo(async () => { @@ -96,7 +100,8 @@ export const useAzoriusProposals = () => { return; } - return erc721StrategyContract?.getEvents.Voted(); + const events = await erc721StrategyContract.getEvents.Voted(); + return events; }, [erc721StrategyContract]); const executedEvents = useMemo(async () => { @@ -104,9 +109,7 @@ export const useAzoriusProposals = () => { return; } - const filter = azoriusContract.filters.ProposalExecuted(); - const events = await azoriusContract.queryFilter(filter); - + const events = await azoriusContract.getEvents.ProposalExecuted(); return events; }, [azoriusContract]); @@ -122,7 +125,7 @@ export const useAzoriusProposals = () => { const loadAzoriusProposals = useCallback( async ( - _azoriusContract: Azorius | undefined, + _azoriusContract: GetContractReturnType | undefined, _erc20StrategyContract: | GetContractReturnType | undefined, @@ -130,13 +133,15 @@ export const useAzoriusProposals = () => { | GetContractReturnType | undefined, _strategyType: VotingStrategyType | undefined, - _erc20VotedEvents: Promise< - GetContractEventsReturnType | undefined - >, - _erc721VotedEvents: Promise< - GetContractEventsReturnType | undefined - >, - _executedEvents: Promise, + _erc20VotedEvents: + | GetContractEventsReturnType + | undefined, + _erc721VotedEvents: + | GetContractEventsReturnType + | undefined, + _executedEvents: + | GetContractEventsReturnType + | undefined, _provider: Providers | undefined, _decode: ( value: string, @@ -148,11 +153,7 @@ export const useAzoriusProposals = () => { if (!_strategyType || !_azoriusContract || !_provider) { return; } - - const proposalCreatedFilter = _azoriusContract.filters.ProposalCreated(); - const proposalCreatedEvents = ( - await _azoriusContract.queryFilter(proposalCreatedFilter) - ).reverse(); + const proposalCreatedEvents = (await _azoriusContract.getEvents.ProposalCreated()).reverse(); for (const proposalCreatedEvent of proposalCreatedEvents) { let proposalData; @@ -162,34 +163,32 @@ export const useAzoriusProposals = () => { proposalCreatedEvent.args.metadata, ); - const decodedTransactions = await decodeTransactions( - _decode, - proposalCreatedEvent.args.transactions.map(t => ({ - ...t, - to: getAddress(t.to), - // @dev if decodeTransactions worked - we can be certain that this is Hex so type casting should be save. - // Also this will change and this casting won't be needed after migrating to viem's getContract - data: t.data as Hex, - value: t.value.toBigInt(), - })), - ); + let transactions: MetaTransaction[] = []; + let decodedTransactions: DecodedTransaction[] = []; + + if (proposalCreatedEvent.args.transactions) { + transactions = proposalCreatedEvent.args.transactions.map(t => ({ + to: t.to, + data: t.data, + value: t.value, + operation: t.operation, + })); + + decodedTransactions = await decodeTransactions(_decode, transactions); + } + proposalData = { metaData: { title: metadataEvent.title, description: metadataEvent.description, documentationUrl: metadataEvent.documentationUrl, }, - transactions: proposalCreatedEvent.args.transactions.map(t => ({ - ...t, - to: getAddress(t.to), - value: t.value.toBigInt(), - data: t.data as Hex, // @dev Same here - })), + transactions, decodedTransactions, }; } catch { logError( - 'Unable to parse proposal metadata or transactions.', + 'Unable to parse proposal metadata or transactions', 'metadata:', proposalCreatedEvent.args.metadata, 'transactions:', @@ -198,11 +197,15 @@ export const useAzoriusProposals = () => { } } + if (!proposalCreatedEvent.args.proposalId || !proposalCreatedEvent.args.proposer) { + continue; + } + const proposal = await mapProposalCreatedEventToProposal( _erc20StrategyContract, _erc721StrategyContract, _strategyType, - proposalCreatedEvent.args.proposalId.toBigInt(), + Number(proposalCreatedEvent.args.proposalId), proposalCreatedEvent.args.proposer, _azoriusContract, _provider, @@ -224,15 +227,15 @@ export const useAzoriusProposals = () => { [azoriusContractAddress], ); - return (proposalLoaded: (proposal: AzoriusProposal) => void) => { + return async (proposalLoaded: (proposal: AzoriusProposal) => void) => { return loadAzoriusProposals( azoriusContract, erc20StrategyContract, erc721StrategyContract, strategyType, - erc20VotedEvents, - erc721VotedEvents, - executedEvents, + await erc20VotedEvents, + await erc721VotedEvents, + await executedEvents, provider, decode, proposalLoaded, diff --git a/src/hooks/DAO/loaders/governance/useERC20LinearStrategy.ts b/src/hooks/DAO/loaders/governance/useERC20LinearStrategy.ts index 304b3aecfc..dc35218fb4 100644 --- a/src/hooks/DAO/loaders/governance/useERC20LinearStrategy.ts +++ b/src/hooks/DAO/loaders/governance/useERC20LinearStrategy.ts @@ -1,6 +1,7 @@ import { useCallback, useEffect, useMemo } from 'react'; import { getAddress, getContract } from 'viem'; import { usePublicClient } from 'wagmi'; +import AzoriusAbi from '../../../../assets/abi/Azorius'; import LinearERC20VotingAbi from '../../../../assets/abi/LinearERC20Voting'; import { useFractal } from '../../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; @@ -33,18 +34,27 @@ export const useERC20LinearStrategy = () => { }, [ozLinearVotingContractAddress, publicClient]); const loadERC20Strategy = useCallback(async () => { - if (!ozLinearVotingContract || !azoriusContractAddress || !provider || !baseContracts) { + if ( + !ozLinearVotingContract || + !azoriusContractAddress || + !provider || + !baseContracts || + !publicClient + ) { return {}; } - const azoriusContract = - baseContracts.fractalAzoriusMasterCopyContract.asProvider.attach(azoriusContractAddress); + const azoriusContract = getContract({ + abi: AzoriusAbi, + address: getAddress(azoriusContractAddress), + client: publicClient, + }); const [votingPeriodBlocks, quorumNumerator, quorumDenominator, timeLockPeriod] = await Promise.all([ ozLinearVotingContract.read.votingPeriod(), ozLinearVotingContract.read.quorumNumerator(), ozLinearVotingContract.read.QUORUM_DENOMINATOR(), - azoriusContract.timelockPeriod(), + azoriusContract.read.timelockPeriod(), ]); const quorumPercentage = (quorumNumerator * 100n) / quorumDenominator; @@ -73,6 +83,7 @@ export const useERC20LinearStrategy = () => { getTimeDuration, ozLinearVotingContract, provider, + publicClient, ]); useEffect(() => { diff --git a/src/hooks/DAO/loaders/governance/useERC721LinearStrategy.ts b/src/hooks/DAO/loaders/governance/useERC721LinearStrategy.ts index ce478e6b31..778a5e2ab1 100644 --- a/src/hooks/DAO/loaders/governance/useERC721LinearStrategy.ts +++ b/src/hooks/DAO/loaders/governance/useERC721LinearStrategy.ts @@ -1,6 +1,7 @@ import { useCallback, useEffect, useMemo } from 'react'; import { getAddress, getContract } from 'viem'; import { usePublicClient } from 'wagmi'; +import AzoriusAbi from '../../../../assets/abi/Azorius'; import LinearERC721VotingAbi from '../../../../assets/abi/LinearERC721Voting'; import { useFractal } from '../../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; @@ -33,17 +34,26 @@ export const useERC721LinearStrategy = () => { }, [erc721LinearVotingContractAddress, publicClient]); const loadERC721Strategy = useCallback(async () => { - if (!azoriusContractAddress || !provider || !baseContracts || !erc721LinearVotingContract) { + if ( + !azoriusContractAddress || + !provider || + !baseContracts || + !erc721LinearVotingContract || + !publicClient + ) { return {}; } - const azoriusContract = - baseContracts.fractalAzoriusMasterCopyContract.asProvider.attach(azoriusContractAddress); + const azoriusContract = getContract({ + abi: AzoriusAbi, + address: getAddress(azoriusContractAddress), + client: publicClient, + }); const [votingPeriodBlocks, quorumThreshold, timeLockPeriod] = await Promise.all([ erc721LinearVotingContract.read.votingPeriod(), erc721LinearVotingContract.read.quorumThreshold(), - azoriusContract.timelockPeriod(), + azoriusContract.read.timelockPeriod(), ]); const votingPeriodValue = await blocksToSeconds(votingPeriodBlocks, provider); @@ -71,6 +81,7 @@ export const useERC721LinearStrategy = () => { erc721LinearVotingContract, getTimeDuration, provider, + publicClient, ]); useEffect(() => { diff --git a/src/hooks/DAO/loaders/useFractalGuardContracts.ts b/src/hooks/DAO/loaders/useFractalGuardContracts.ts index b8b6be1438..2fb722a4ab 100644 --- a/src/hooks/DAO/loaders/useFractalGuardContracts.ts +++ b/src/hooks/DAO/loaders/useFractalGuardContracts.ts @@ -1,10 +1,8 @@ -import { - Azorius, - AzoriusFreezeGuard, - MultisigFreezeGuard, -} from '@fractal-framework/fractal-contracts'; +import { AzoriusFreezeGuard, MultisigFreezeGuard } from '@fractal-framework/fractal-contracts'; import { useCallback, useEffect, useRef } from 'react'; -import { getAddress, zeroAddress } from 'viem'; +import { getAddress, getContract, zeroAddress } from 'viem'; +import { usePublicClient } from 'wagmi'; +import AzoriusAbi from '../../../assets/abi/Azorius'; import { useFractal } from '../../../providers/App/AppProvider'; import { GuardContractAction } from '../../../providers/App/guardContracts/action'; import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; @@ -31,6 +29,8 @@ export const useFractalGuardContracts = ({ loadOnMount = true }: { loadOnMount?: const { getZodiacModuleProxyMasterCopyData } = useMasterCopy(); + const publicClient = usePublicClient(); + const loadFractalGuardContracts = useCallback( async ( _daoAddress: string, @@ -52,9 +52,13 @@ export const useFractalGuardContracts = ({ loadOnMount = true }: { loadOnMount?: const azoriusModule = _fractalModules?.find( module => module.moduleType === FractalModuleType.AZORIUS, ); - if (!!azoriusModule && azoriusModule.moduleContract) { - const azoriusGuardAddress = await (azoriusModule.moduleContract as Azorius).getGuard(); - + if (azoriusModule && publicClient) { + const azoriusContract = getContract({ + abi: AzoriusAbi, + address: getAddress(azoriusModule.moduleAddress), + client: publicClient, + }); + const azoriusGuardAddress = await azoriusContract.read.getGuard(); if (azoriusGuardAddress === zeroAddress) { return { freezeGuardContractAddress: '', @@ -109,7 +113,7 @@ export const useFractalGuardContracts = ({ loadOnMount = true }: { loadOnMount?: }; } }, - [baseContracts, getZodiacModuleProxyMasterCopyData], + [baseContracts, getZodiacModuleProxyMasterCopyData, publicClient], ); const setGuardContracts = useCallback(async () => { diff --git a/src/hooks/DAO/loaders/useFractalModules.ts b/src/hooks/DAO/loaders/useFractalModules.ts index a278a4a62f..b0b66e2c28 100644 --- a/src/hooks/DAO/loaders/useFractalModules.ts +++ b/src/hooks/DAO/loaders/useFractalModules.ts @@ -1,13 +1,10 @@ import { useCallback } from 'react'; -import { getAddress, getContract } from 'viem'; +import { getAddress } from 'viem'; import { usePublicClient } from 'wagmi'; -import FractalModuleAbi from '../../../assets/abi/FractalModule'; -import { useFractal } from '../../../providers/App/AppProvider'; import { FractalModuleData, FractalModuleType } from '../../../types'; import { useMasterCopy } from '../../utils/useMasterCopy'; export const useFractalModules = () => { - const { baseContracts } = useFractal(); const { getZodiacModuleProxyMasterCopyData } = useMasterCopy(); const publicClient = usePublicClient(); const lookupModules = useCallback( @@ -20,26 +17,18 @@ export const useFractalModules = () => { let safeModule: FractalModuleData; - if (masterCopyData.isAzorius && baseContracts) { + if (masterCopyData.isAzorius && publicClient) { safeModule = { - moduleContract: - baseContracts.fractalAzoriusMasterCopyContract.asSigner.attach(moduleAddress), moduleAddress: moduleAddress, moduleType: FractalModuleType.AZORIUS, }; } else if (masterCopyData.isFractalModule && publicClient) { safeModule = { - moduleContract: getContract({ - abi: FractalModuleAbi, - address: getAddress(moduleAddress), - client: publicClient, - }), moduleAddress: moduleAddress, moduleType: FractalModuleType.FRACTAL, }; } else { safeModule = { - moduleContract: undefined, moduleAddress: moduleAddress, moduleType: FractalModuleType.UNKNOWN, }; @@ -50,7 +39,7 @@ export const useFractalModules = () => { ); return modules; }, - [baseContracts, getZodiacModuleProxyMasterCopyData, publicClient], + [getZodiacModuleProxyMasterCopyData, publicClient], ); return lookupModules; }; diff --git a/src/hooks/DAO/loaders/useGovernanceContracts.ts b/src/hooks/DAO/loaders/useGovernanceContracts.ts index 5fc4c4df79..d3c8d17614 100644 --- a/src/hooks/DAO/loaders/useGovernanceContracts.ts +++ b/src/hooks/DAO/loaders/useGovernanceContracts.ts @@ -1,16 +1,15 @@ -import { Azorius } from '@fractal-framework/fractal-contracts'; import { useCallback, useEffect, useRef } from 'react'; -import { getContract, getAddress } from 'viem'; +import { getContract, Address } from 'viem'; import { usePublicClient } from 'wagmi'; import LinearERC20VotingAbi from '../../../assets/abi/LinearERC20Voting'; import LockReleaseAbi from '../../../assets/abi/LockRelease'; import VotesERC20WrapperAbi from '../../../assets/abi/VotesERC20Wrapper'; -import { SENTINEL_ADDRESS } from '../../../constants/common'; import { useFractal } from '../../../providers/App/AppProvider'; import { GovernanceContractAction } from '../../../providers/App/governanceContracts/action'; import { getAzoriusModuleFromModules } from '../../../utils'; import useSafeContracts from '../../safe/useSafeContracts'; import { useMasterCopy } from '../../utils/useMasterCopy'; +import useVotingStrategyAddress from '../../utils/useVotingStrategyAddress'; export const useGovernanceContracts = () => { // tracks the current valid DAO address; helps prevent unnecessary calls @@ -20,105 +19,105 @@ export const useGovernanceContracts = () => { const { getZodiacModuleProxyMasterCopyData } = useMasterCopy(); const publicClient = usePublicClient(); + const { getVotingStrategyAddress } = useVotingStrategyAddress(); + const { fractalModules, isModulesLoaded, daoAddress } = node; const loadGovernanceContracts = useCallback(async () => { if (!baseContracts || !publicClient) { return; } - const { fractalAzoriusMasterCopyContract } = baseContracts; const azoriusModule = getAzoriusModuleFromModules(fractalModules); - const azoriusModuleContract = azoriusModule?.moduleContract as Azorius; - - if (!!azoriusModuleContract) { - const azoriusContract = fractalAzoriusMasterCopyContract.asProvider.attach( - azoriusModuleContract.address, - ); - let ozLinearVotingContractAddress: string | undefined; - let erc721LinearVotingContractAddress: string | undefined; - let votesTokenContractAddress: string | undefined; - let underlyingTokenAddress: string | undefined; - let lockReleaseContractAddress: string | undefined; - - // @dev assumes the first strategy is the voting contract - const votingStrategyAddress = (await azoriusContract.getStrategies(SENTINEL_ADDRESS, 0))[1]; - - const masterCopyData = await getZodiacModuleProxyMasterCopyData( - getAddress(votingStrategyAddress), - ); - const isOzLinearVoting = masterCopyData.isOzLinearVoting; - const isOzLinearVotingERC721 = masterCopyData.isOzLinearVotingERC721; - - if (isOzLinearVoting) { - ozLinearVotingContractAddress = votingStrategyAddress; - - const ozLinearVotingContract = getContract({ - abi: LinearERC20VotingAbi, - address: getAddress(ozLinearVotingContractAddress), - client: publicClient, - }); - const govTokenAddress = await ozLinearVotingContract.read.governanceToken(); - - const possibleERC20Wrapper = getContract({ - abi: VotesERC20WrapperAbi, - address: getAddress(govTokenAddress), - client: publicClient, - }); - - underlyingTokenAddress = await possibleERC20Wrapper.read.underlying().catch(() => { - // if the underlying token is not an ERC20Wrapper, this will throw an error, - // so we catch it and return undefined - return undefined; - }); - const possibleLockRelease = getContract({ - address: getAddress(govTokenAddress), - abi: LockReleaseAbi, - client: { public: publicClient }, - }); - - let lockedTokenAddress = undefined; - try { - lockedTokenAddress = await possibleLockRelease.read.token(); - } catch { - // no-op - // if the underlying token is not an ERC20Wrapper, this will throw an error, - // so we catch it and do nothing - } - - if (lockedTokenAddress) { - lockReleaseContractAddress = govTokenAddress; - // @dev if the underlying token is an ERC20Wrapper, we use the underlying token as the token contract - votesTokenContractAddress = lockedTokenAddress; - } else { - // @dev if the no underlying token, we use the governance token as the token contract - votesTokenContractAddress = govTokenAddress; - } - } else if (isOzLinearVotingERC721) { - // @dev for use with the ERC721 voting contract - erc721LinearVotingContractAddress = votingStrategyAddress; + + const votingStrategyAddress = await getVotingStrategyAddress(); + + if (!azoriusModule || !votingStrategyAddress) { + action.dispatch({ + type: GovernanceContractAction.SET_GOVERNANCE_CONTRACT_ADDRESSES, + payload: {}, + }); + return; + } + + let ozLinearVotingContractAddress: Address | undefined; + let erc721LinearVotingContractAddress: string | undefined; + let votesTokenContractAddress: string | undefined; + let underlyingTokenAddress: string | undefined; + let lockReleaseContractAddress: string | undefined; + + const { isOzLinearVoting, isOzLinearVotingERC721 } = + await getZodiacModuleProxyMasterCopyData(votingStrategyAddress); + + if (isOzLinearVoting) { + ozLinearVotingContractAddress = votingStrategyAddress; + + const ozLinearVotingContract = getContract({ + abi: LinearERC20VotingAbi, + address: ozLinearVotingContractAddress, + client: publicClient, + }); + const govTokenAddress = await ozLinearVotingContract.read.governanceToken(); + + const possibleERC20Wrapper = getContract({ + abi: VotesERC20WrapperAbi, + address: govTokenAddress, + client: publicClient, + }); + + underlyingTokenAddress = await possibleERC20Wrapper.read.underlying().catch(() => { + // if the underlying token is not an ERC20Wrapper, this will throw an error, + // so we catch it and return undefined + return undefined; + }); + const possibleLockRelease = getContract({ + address: govTokenAddress, + abi: LockReleaseAbi, + client: { public: publicClient }, + }); + + let lockedTokenAddress = undefined; + try { + lockedTokenAddress = await possibleLockRelease.read.token(); + } catch { + // no-op + // if the underlying token is not an ERC20Wrapper, this will throw an error, + // so we catch it and do nothing } - if (votesTokenContractAddress || erc721LinearVotingContractAddress) { - action.dispatch({ - type: GovernanceContractAction.SET_GOVERNANCE_CONTRACT, - payload: { - ozLinearVotingContractAddress, - erc721LinearVotingContractAddress, - azoriusContractAddress: azoriusModuleContract.address, - votesTokenContractAddress, - underlyingTokenAddress, - lockReleaseContractAddress, - }, - }); + if (lockedTokenAddress) { + lockReleaseContractAddress = govTokenAddress; + // @dev if the underlying token is an ERC20Wrapper, we use the underlying token as the token contract + votesTokenContractAddress = lockedTokenAddress; + } else { + // @dev if the no underlying token, we use the governance token as the token contract + votesTokenContractAddress = govTokenAddress; } - // else this has no governance token and can be assumed is a multi-sig - } else { + } else if (isOzLinearVotingERC721) { + // @dev for use with the ERC721 voting contract + erc721LinearVotingContractAddress = votingStrategyAddress; + } + + if (votesTokenContractAddress || erc721LinearVotingContractAddress) { action.dispatch({ - type: GovernanceContractAction.SET_GOVERNANCE_CONTRACT, - payload: {}, + type: GovernanceContractAction.SET_GOVERNANCE_CONTRACT_ADDRESSES, + payload: { + ozLinearVotingContractAddress, + erc721LinearVotingContractAddress, + azoriusContractAddress: azoriusModule.moduleAddress, + votesTokenContractAddress, + underlyingTokenAddress, + lockReleaseContractAddress, + }, }); } - }, [action, getZodiacModuleProxyMasterCopyData, baseContracts, fractalModules, publicClient]); + }, [ + action, + baseContracts, + fractalModules, + getVotingStrategyAddress, + getZodiacModuleProxyMasterCopyData, + publicClient, + ]); useEffect(() => { if (currentValidAddress.current !== daoAddress && isModulesLoaded) { diff --git a/src/hooks/DAO/loaders/useProposals.ts b/src/hooks/DAO/loaders/useProposals.ts index fdc58d6219..59f6e0221d 100644 --- a/src/hooks/DAO/loaders/useProposals.ts +++ b/src/hooks/DAO/loaders/useProposals.ts @@ -21,7 +21,7 @@ export const useDAOProposals = () => { clearIntervals(); if (type === GovernanceType.AZORIUS_ERC20 || type === GovernanceType.AZORIUS_ERC721) { // load Azorius proposals and strategies - loadAzoriusProposals(proposal => { + await loadAzoriusProposals(proposal => { action.dispatch({ type: FractalGovernanceAction.SET_AZORIUS_PROPOSAL, payload: proposal, diff --git a/src/hooks/DAO/proposal/useExecuteProposal.ts b/src/hooks/DAO/proposal/useExecuteProposal.ts index 9b5cbd09fc..fc9b8a8e17 100644 --- a/src/hooks/DAO/proposal/useExecuteProposal.ts +++ b/src/hooks/DAO/proposal/useExecuteProposal.ts @@ -1,8 +1,10 @@ import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; +import { Address, Hex, getAddress, getContract } from 'viem'; +import { useWalletClient } from 'wagmi'; +import AzoriusAbi from '../../../assets/abi/Azorius'; import { useFractal } from '../../../providers/App/AppProvider'; import { MetaTransaction, FractalProposal, AzoriusProposal } from '../../../types'; -import useSafeContracts from '../../safe/useSafeContracts'; import { useTransaction } from '../../utils/useTransaction'; import useUpdateProposalState from './useUpdateProposalState'; @@ -11,12 +13,12 @@ export default function useExecuteProposal() { const { governanceContracts, action } = useFractal(); const { azoriusContractAddress } = governanceContracts; - const baseContracts = useSafeContracts(); const updateProposalState = useUpdateProposalState({ governanceContracts, governanceDispatch: action.dispatch, }); - const [contractCallExecuteProposal, contractCallPending] = useTransaction(); + const { data: walletClient } = useWalletClient(); + const [, contractCallPending, contractCallViem] = useTransaction(); const executeProposal = useCallback( (proposal: FractalProposal) => { @@ -25,16 +27,20 @@ export default function useExecuteProposal() { !azoriusContractAddress || !azoriusProposal.data || !azoriusProposal.data.transactions || - !baseContracts + !walletClient ) { return; } - const azoriusContract = - baseContracts.fractalAzoriusMasterCopyContract.asSigner.attach(azoriusContractAddress); - const targets: string[] = []; + const azoriusContract = getContract({ + abi: AzoriusAbi, + address: getAddress(azoriusContractAddress), + client: walletClient, + }); + + const targets: Address[] = []; const values: MetaTransaction['value'][] = []; - const data: string[] = []; + const data: Hex[] = []; const operations: number[] = []; azoriusProposal.data.transactions.forEach(tx => { @@ -44,19 +50,25 @@ export default function useExecuteProposal() { operations.push(tx.operation); }); - contractCallExecuteProposal({ + contractCallViem({ contractFn: () => - azoriusContract.executeProposal(proposal.proposalId, targets, values, data, operations), + azoriusContract.write.executeProposal([ + Number(proposal.proposalId), + targets, + values, + data, + operations, + ]), pendingMessage: t('pendingExecute'), failedMessage: t('failedExecute'), successMessage: t('successExecute'), successCallback: async () => { // @todo may need to re-add a loader here - updateProposalState(BigInt(proposal.proposalId)); + updateProposalState(Number(proposal.proposalId)); }, }); }, - [contractCallExecuteProposal, t, azoriusContractAddress, updateProposalState, baseContracts], + [azoriusContractAddress, contractCallViem, t, updateProposalState, walletClient], ); return { diff --git a/src/hooks/DAO/proposal/useSubmitProposal.ts b/src/hooks/DAO/proposal/useSubmitProposal.ts index 58f8fc7789..9e8eb359a5 100644 --- a/src/hooks/DAO/proposal/useSubmitProposal.ts +++ b/src/hooks/DAO/proposal/useSubmitProposal.ts @@ -1,4 +1,3 @@ -import { Azorius } from '@fractal-framework/fractal-contracts'; import axios from 'axios'; import { useCallback, useMemo, useState } from 'react'; import { toast } from 'react-toastify'; @@ -9,20 +8,23 @@ import { parseAbiParameters, isHex, encodeFunctionData, + getContract, + Address, } from 'viem'; -import { useWalletClient } from 'wagmi'; +import { usePublicClient, useWalletClient } from 'wagmi'; +import AzoriusAbi from '../../../assets/abi/Azorius'; import MultiSendCallOnlyAbi from '../../../assets/abi/MultiSendCallOnly'; -import { ADDRESS_MULTISIG_METADATA, SENTINEL_ADDRESS } from '../../../constants/common'; +import { ADDRESS_MULTISIG_METADATA } from '../../../constants/common'; import { buildSafeAPIPost, encodeMultiSend } from '../../../helpers'; import { logError } from '../../../helpers/errorLogging'; import { useFractal } from '../../../providers/App/AppProvider'; import useIPFSClient from '../../../providers/App/hooks/useIPFSClient'; import { useSafeAPI } from '../../../providers/App/hooks/useSafeAPI'; -import { useEthersSigner } from '../../../providers/Ethers/hooks/useEthersSigner'; import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; import { MetaTransaction, ProposalExecuteData, CreateProposalMetadata } from '../../../types'; import { buildSafeApiUrl, getAzoriusModuleFromModules } from '../../../utils'; import useSignerOrProvider from '../../utils/useSignerOrProvider'; +import useVotingStrategyAddress from '../../utils/useVotingStrategyAddress'; import { useFractalModules } from '../loaders/useFractalModules'; import { useDAOProposals } from '../loaders/useProposals'; @@ -33,32 +35,21 @@ interface ISubmitProposal { failedToastMessage: string; successToastMessage: string; successCallback?: (addressPrefix: string, daoAddress: string) => void; - /** - * @param safeAddress - provided address of DAO to which proposal will be submitted - */ safeAddress?: string; } interface ISubmitAzoriusProposal extends ISubmitProposal { - /** - * @param azoriusContract - provided Azorius contract. - * Depending on safeAddress it's either picked from global context - * either grabbed from the safe info from Safe API. - */ - azoriusContract: Azorius; - /** - * @param votingStrategyAddress - provided voting strategy address for proposal submission. - * Depending on safeAddress it's either picked from global context - * either grabbed from the safe info from Safe API & provided Azorius contract. - */ + azoriusAddress: Address; votingStrategyAddress: string; } export default function useSubmitProposal() { const [pendingCreateTx, setPendingCreateTx] = useState(false); const loadDAOProposals = useDAOProposals(); - const signer = useEthersSigner(); const { data: walletClient } = useWalletClient(); + const publicClient = usePublicClient(); + + const { getVotingStrategyAddress } = useVotingStrategyAddress(); const { node: { safe, fractalModules }, @@ -68,16 +59,17 @@ export default function useSubmitProposal() { const safeAPI = useSafeAPI(); const globalAzoriusContract = useMemo(() => { - if (!signer) { - return undefined; - } const azoriusModule = getAzoriusModuleFromModules(fractalModules); - if (!azoriusModule) { - return undefined; + if (!azoriusModule || !walletClient) { + return; } - const moduleContract = azoriusModule.moduleContract as Azorius; - return moduleContract.connect(signer); - }, [fractalModules, signer]); + + return getContract({ + abi: AzoriusAbi, + address: getAddress(azoriusModule.moduleAddress), + client: walletClient, + }); + }, [fractalModules, walletClient]); const lookupModules = useFractalModules(); const signerOrProvider = useSignerOrProvider(); @@ -206,7 +198,7 @@ export default function useSubmitProposal() { const submitAzoriusProposal = useCallback( async ({ proposalData, - azoriusContract, + azoriusAddress, votingStrategyAddress, pendingToastMessage, successToastMessage, @@ -214,7 +206,7 @@ export default function useSubmitProposal() { failedToastMessage, safeAddress, }: ISubmitAzoriusProposal) => { - if (!proposalData) { + if (!proposalData || !walletClient || !publicClient) { return; } const toastId = toast(pendingToastMessage, { @@ -234,19 +226,26 @@ export default function useSubmitProposal() { operation: 0, })); + const azoriusContract = getContract({ + abi: AzoriusAbi, + address: azoriusAddress, + client: walletClient, + }); + // @todo: Implement voting strategy proposal selection when/if we will support multiple strategies on single Azorius instance - await ( - await azoriusContract.submitProposal( - votingStrategyAddress, - '0x', - transactions, - JSON.stringify({ - title: proposalData.metaData.title, - description: proposalData.metaData.description, - documentationUrl: proposalData.metaData.documentationUrl, - }), - ) - ).wait(); + const txHash = await azoriusContract.write.submitProposal([ + getAddress(votingStrategyAddress), + '0x', + transactions, + JSON.stringify({ + title: proposalData.metaData.title, + description: proposalData.metaData.description, + documentationUrl: proposalData.metaData.documentationUrl, + }), + ]); + + await publicClient.waitForTransactionReceipt({ hash: txHash }); + toast.dismiss(toastId); toast(successToastMessage); if (successCallback) { @@ -260,7 +259,7 @@ export default function useSubmitProposal() { setPendingCreateTx(false); } }, - [addressPrefix], + [addressPrefix, publicClient, walletClient], ); const submitProposal = useCallback( @@ -279,10 +278,11 @@ export default function useSubmitProposal() { if (safeAddress && isAddress(safeAddress)) { // Submitting proposal to any DAO out of global context + const votingStrategyAddress = await getVotingStrategyAddress(getAddress(safeAddress)); const safeInfo = await safeAPI.getSafeInfo(getAddress(safeAddress)); const modules = await lookupModules(safeInfo.modules); const azoriusModule = getAzoriusModuleFromModules(modules); - if (!azoriusModule) { + if (!azoriusModule || !votingStrategyAddress) { await submitMultisigProposal({ proposalData, pendingToastMessage, @@ -293,12 +293,7 @@ export default function useSubmitProposal() { safeAddress, }); } else { - const azoriusModuleContract = azoriusModule.moduleContract as Azorius; - // @dev assumes the first strategy is the voting contract - const votingStrategyAddress = ( - await azoriusModuleContract.getStrategies(SENTINEL_ADDRESS, 0) - )[1]; - submitAzoriusProposal({ + await submitAzoriusProposal({ proposalData, pendingToastMessage, successToastMessage, @@ -306,7 +301,7 @@ export default function useSubmitProposal() { nonce, successCallback, safeAddress, - azoriusContract: azoriusModule.moduleContract as Azorius, + azoriusAddress: getAddress(azoriusModule.moduleAddress), votingStrategyAddress, }); } @@ -335,22 +330,23 @@ export default function useSubmitProposal() { nonce, successCallback, votingStrategyAddress, - azoriusContract: globalAzoriusContract, + azoriusAddress: globalAzoriusContract.address, safeAddress: safe?.address, }); } } }, [ - globalAzoriusContract, + erc721LinearVotingContractAddress, freezeVotingContractAddress, - safe, + getVotingStrategyAddress, + globalAzoriusContract, lookupModules, - submitMultisigProposal, ozLinearVotingContractAddress, - erc721LinearVotingContractAddress, - submitAzoriusProposal, + safe?.address, safeAPI, + submitAzoriusProposal, + submitMultisigProposal, ], ); diff --git a/src/hooks/DAO/proposal/useUpdateProposalState.ts b/src/hooks/DAO/proposal/useUpdateProposalState.ts index dd7535bfc5..edc3140bd9 100644 --- a/src/hooks/DAO/proposal/useUpdateProposalState.ts +++ b/src/hooks/DAO/proposal/useUpdateProposalState.ts @@ -1,11 +1,13 @@ import { useCallback, Dispatch } from 'react'; +import { getAddress, getContract } from 'viem'; +import { usePublicClient } from 'wagmi'; +import AzoriusAbi from '../../../assets/abi/Azorius'; import { FractalGovernanceAction, FractalGovernanceActions, } from '../../../providers/App/governance/action'; import { FractalGovernanceContracts } from '../../../types'; import { getAzoriusProposalState } from '../../../utils'; -import useSafeContracts from '../../safe/useSafeContracts'; interface IUseUpdateProposalState { governanceContracts: FractalGovernanceContracts; @@ -16,21 +18,25 @@ export default function useUpdateProposalState({ governanceContracts: { azoriusContractAddress }, governanceDispatch, }: IUseUpdateProposalState) { - const baseContracts = useSafeContracts(); + const publicClient = usePublicClient(); const updateProposalState = useCallback( - async (proposalId: bigint) => { - if (!azoriusContractAddress || !baseContracts) { + async (proposalId: number) => { + if (!azoriusContractAddress || !publicClient) { return; } - const azoriusContract = - baseContracts.fractalAzoriusMasterCopyContract.asProvider.attach(azoriusContractAddress); + const azoriusContract = getContract({ + abi: AzoriusAbi, + address: getAddress(azoriusContractAddress), + client: publicClient, + }); + const newState = await getAzoriusProposalState(azoriusContract, proposalId); governanceDispatch({ type: FractalGovernanceAction.UPDATE_PROPOSAL_STATE, payload: { proposalId: proposalId.toString(), state: newState }, }); }, - [azoriusContractAddress, governanceDispatch, baseContracts], + [azoriusContractAddress, governanceDispatch, publicClient], ); return updateProposalState; diff --git a/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts b/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts index 90c93e03e7..6138c34e8d 100644 --- a/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts +++ b/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts @@ -1,16 +1,13 @@ -import { Azorius } from '@fractal-framework/fractal-contracts'; import { useState, useEffect, useCallback } from 'react'; import { GetContractReturnType, PublicClient, erc721Abi, getAddress, getContract } from 'viem'; import { usePublicClient } from 'wagmi'; import LinearERC721VotingAbi from '../../../assets/abi/LinearERC721Voting'; -import { SENTINEL_ADDRESS } from '../../../constants/common'; import { useFractal } from '../../../providers/App/AppProvider'; import { useSafeAPI } from '../../../providers/App/hooks/useSafeAPI'; import { AzoriusGovernance } from '../../../types'; -import { getAzoriusModuleFromModules } from '../../../utils'; import useSafeContracts from '../../safe/useSafeContracts'; import useSignerOrProvider from '../../utils/useSignerOrProvider'; -import { useFractalModules } from '../loaders/useFractalModules'; +import useVotingStrategyAddress from '../../utils/useVotingStrategyAddress'; /** * Retrieves list of ERC-721 voting tokens for the supplied `address`(aka `user.address`) param @@ -40,10 +37,11 @@ export default function useUserERC721VotingTokens( readOnly: { user }, } = useFractal(); const baseContracts = useSafeContracts(); - const lookupModules = useFractalModules(); const safeAPI = useSafeAPI(); const publicClient = usePublicClient(); + const { getVotingStrategyAddress } = useVotingStrategyAddress(); + const azoriusGovernance = governance as AzoriusGovernance; const { erc721Tokens } = azoriusGovernance; @@ -71,18 +69,11 @@ export default function useUserERC721VotingTokens( if (_safeAddress && daoAddress !== _safeAddress) { // Means getting these for any safe, primary use case - calculating user voting weight for freeze voting - const safeInfo = await safeAPI.getSafeInfo(getAddress(_safeAddress)); - const safeModules = await lookupModules(safeInfo.modules); - const azoriusModule = getAzoriusModuleFromModules(safeModules); - if (azoriusModule && azoriusModule.moduleContract) { - const azoriusContract = azoriusModule.moduleContract as Azorius; - // @dev assumes the first strategy is the voting contract - const votingContractAddress = ( - await azoriusContract.getStrategies(SENTINEL_ADDRESS, 0) - )[1]; + const votingStrategyAddress = await getVotingStrategyAddress(getAddress(_safeAddress)); + if (votingStrategyAddress) { votingContract = getContract({ abi: LinearERC721VotingAbi, - address: getAddress(votingContractAddress), + address: votingStrategyAddress, client: publicClient, }); const addresses = await votingContract.read.getAllTokenAddresses(); @@ -213,7 +204,7 @@ export default function useUserERC721VotingTokens( daoAddress, erc721LinearVotingContractAddress, erc721Tokens, - lookupModules, + getVotingStrategyAddress, publicClient, safeAPI, signerOrProvider, diff --git a/src/hooks/DAO/useBuildDAOTx.ts b/src/hooks/DAO/useBuildDAOTx.ts index f4d317e33c..35613f5b6c 100644 --- a/src/hooks/DAO/useBuildDAOTx.ts +++ b/src/hooks/DAO/useBuildDAOTx.ts @@ -32,6 +32,7 @@ const useBuildDAOTx = () => { fractalModuleMasterCopy, linearVotingMasterCopy: linearERC20VotingMasterCopy, linearVotingERC721MasterCopy: linearERC721VotingMasterCopy, + fractalAzoriusMasterCopy: azoriusMasterCopy, }, } = useNetworkConfig(); @@ -55,7 +56,6 @@ const useBuildDAOTx = () => { return; } const { - fractalAzoriusMasterCopyContract, multisigFreezeGuardMasterCopyContract, azoriusFreezeGuardMasterCopyContract, freezeMultisigVotingMasterCopyContract, @@ -67,12 +67,11 @@ const useBuildDAOTx = () => { daoData.governance === GovernanceType.AZORIUS_ERC20 || daoData.governance === GovernanceType.AZORIUS_ERC721 ) { - if (!fractalAzoriusMasterCopyContract || !azoriusFreezeGuardMasterCopyContract) { + if (!azoriusFreezeGuardMasterCopyContract) { return; } azoriusContracts = { - fractalAzoriusMasterCopyContract: fractalAzoriusMasterCopyContract.asSigner, azoriusFreezeGuardMasterCopyContract: azoriusFreezeGuardMasterCopyContract.asSigner, }; } @@ -103,6 +102,7 @@ const useBuildDAOTx = () => { fractalModuleMasterCopy, linearERC20VotingMasterCopy, linearERC721VotingMasterCopy, + azoriusMasterCopy, parentAddress, parentTokenAddress, ); @@ -161,6 +161,7 @@ const useBuildDAOTx = () => { fractalModuleMasterCopy, linearERC20VotingMasterCopy, linearERC721VotingMasterCopy, + azoriusMasterCopy, ], ); diff --git a/src/hooks/DAO/useDeployAzorius.ts b/src/hooks/DAO/useDeployAzorius.ts index b067b7be18..d66de6d9c9 100644 --- a/src/hooks/DAO/useDeployAzorius.ts +++ b/src/hooks/DAO/useDeployAzorius.ts @@ -38,6 +38,7 @@ const useDeployAzorius = () => { fractalModuleMasterCopy, linearVotingMasterCopy: linearERC20VotingMasterCopy, linearVotingERC721MasterCopy: linearERC721VotingMasterCopy, + fractalAzoriusMasterCopy: azoriusMasterCopy, }, addressPrefix, } = useNetworkConfig(); @@ -62,7 +63,6 @@ const useDeployAzorius = () => { return; } const { - fractalAzoriusMasterCopyContract, multisigFreezeGuardMasterCopyContract, azoriusFreezeGuardMasterCopyContract, freezeMultisigVotingMasterCopyContract, @@ -72,7 +72,6 @@ const useDeployAzorius = () => { let azoriusContracts: AzoriusContracts; azoriusContracts = { - fractalAzoriusMasterCopyContract: fractalAzoriusMasterCopyContract.asProvider, azoriusFreezeGuardMasterCopyContract: azoriusFreezeGuardMasterCopyContract.asProvider, }; @@ -102,6 +101,7 @@ const useDeployAzorius = () => { fractalModuleMasterCopy, linearERC20VotingMasterCopy, linearERC721VotingMasterCopy, + azoriusMasterCopy, undefined, undefined, ); @@ -173,6 +173,7 @@ const useDeployAzorius = () => { fractalModuleMasterCopy, linearERC20VotingMasterCopy, linearERC721VotingMasterCopy, + azoriusMasterCopy, ], ); diff --git a/src/hooks/safe/useSafeContracts.ts b/src/hooks/safe/useSafeContracts.ts index a906cebc61..a0857c34b3 100644 --- a/src/hooks/safe/useSafeContracts.ts +++ b/src/hooks/safe/useSafeContracts.ts @@ -3,7 +3,6 @@ import { ERC20FreezeVoting__factory, MultisigFreezeGuard__factory, MultisigFreezeVoting__factory, - Azorius__factory, ERC721FreezeVoting__factory, } from '@fractal-framework/fractal-contracts'; import { useMemo } from 'react'; @@ -17,7 +16,6 @@ export default function useSafeContracts() { const { contracts: { - fractalAzoriusMasterCopy, multisigFreezeGuardMasterCopy, azoriusFreezeGuardMasterCopy, multisigFreezeVotingMasterCopy, @@ -31,11 +29,6 @@ export default function useSafeContracts() { return; } - const fractalAzoriusMasterCopyContract = { - asSigner: Azorius__factory.connect(fractalAzoriusMasterCopy, signerOrProvider), - asProvider: Azorius__factory.connect(fractalAzoriusMasterCopy, provider), - }; - const multisigFreezeGuardMasterCopyContract = { asSigner: MultisigFreezeGuard__factory.connect( multisigFreezeGuardMasterCopy, @@ -68,7 +61,6 @@ export default function useSafeContracts() { }; return { - fractalAzoriusMasterCopyContract, multisigFreezeGuardMasterCopyContract, azoriusFreezeGuardMasterCopyContract, freezeMultisigVotingMasterCopyContract, @@ -76,7 +68,6 @@ export default function useSafeContracts() { freezeERC721VotingMasterCopyContract, }; }, [ - fractalAzoriusMasterCopy, multisigFreezeGuardMasterCopy, azoriusFreezeGuardMasterCopy, multisigFreezeVotingMasterCopy, diff --git a/src/hooks/utils/useCanUserSubmitProposal.ts b/src/hooks/utils/useCanUserSubmitProposal.ts index 9ac47054ac..00e90f3757 100644 --- a/src/hooks/utils/useCanUserSubmitProposal.ts +++ b/src/hooks/utils/useCanUserSubmitProposal.ts @@ -1,15 +1,12 @@ -import { Azorius } from '@fractal-framework/fractal-contracts'; import { useState, useCallback, useEffect } from 'react'; import { getAddress, getContract } from 'viem'; import { usePublicClient } from 'wagmi'; import LinearERC20VotingAbi from '../../assets/abi/LinearERC20Voting'; import LinearERC721VotingAbi from '../../assets/abi/LinearERC721Voting'; -import { SENTINEL_ADDRESS } from '../../constants/common'; import { useFractal } from '../../providers/App/AppProvider'; import { useSafeAPI } from '../../providers/App/hooks/useSafeAPI'; import { GovernanceType } from '../../types'; -import { getAzoriusModuleFromModules } from '../../utils'; -import { useFractalModules } from '../DAO/loaders/useFractalModules'; +import useVotingStrategyAddress from './useVotingStrategyAddress'; export function useCanUserCreateProposal() { const { @@ -19,11 +16,11 @@ export function useCanUserCreateProposal() { readOnly: { user }, } = useFractal(); const safeAPI = useSafeAPI(); - // const baseContracts = useSafeContracts(); - const lookupModules = useFractalModules(); const [canUserCreateProposal, setCanUserCreateProposal] = useState(); const publicClient = usePublicClient(); + const { getVotingStrategyAddress } = useVotingStrategyAddress(); + /** * Performs a check whether user has access rights to create proposal for DAO * @param {string} safeAddress - parameter to verify that user can create proposal for this specific DAO. @@ -41,24 +38,17 @@ export function useCanUserCreateProposal() { }; if (safeAddress) { - const safeInfo = await safeAPI.getSafeInfo(getAddress(safeAddress)); - const safeModules = await lookupModules(safeInfo.modules); - const azoriusModule = getAzoriusModuleFromModules(safeModules); - - if (azoriusModule && azoriusModule.moduleContract) { - const azoriusContract = azoriusModule.moduleContract as Azorius; - // @dev assumes the first strategy is the voting contract - const votingContractAddress = ( - await azoriusContract.getStrategies(SENTINEL_ADDRESS, 0) - )[1]; + const votingStrategyAddress = await getVotingStrategyAddress(getAddress(safeAddress)); + if (votingStrategyAddress) { const votingContract = getContract({ abi: LinearERC20VotingAbi, - address: getAddress(votingContractAddress), + address: votingStrategyAddress, client: publicClient, }); const isProposer = await votingContract.read.isProposer([getAddress(user.address)]); return isProposer; } else { + const safeInfo = await safeAPI.getSafeInfo(getAddress(safeAddress)); return checkIsMultisigOwner(safeInfo.owners); } } else { @@ -89,7 +79,7 @@ export function useCanUserCreateProposal() { }, [ erc721LinearVotingContractAddress, - lookupModules, + getVotingStrategyAddress, ozLinearVotingContractAddress, publicClient, safe, diff --git a/src/hooks/utils/useMasterCopy.ts b/src/hooks/utils/useMasterCopy.ts index b1dcc8cbf7..e938b8af8e 100644 --- a/src/hooks/utils/useMasterCopy.ts +++ b/src/hooks/utils/useMasterCopy.ts @@ -16,6 +16,7 @@ export function useMasterCopy() { linearVotingMasterCopy, linearVotingERC721MasterCopy, fractalModuleMasterCopy, + fractalAzoriusMasterCopy, }, } = useNetworkConfig(); const publicClient = usePublicClient(); @@ -45,9 +46,8 @@ export function useMasterCopy() { [baseContracts], ); const isAzorius = useCallback( - (masterCopyAddress: Address) => - masterCopyAddress === baseContracts?.fractalAzoriusMasterCopyContract.asProvider.address, - [baseContracts], + (masterCopyAddress: Address) => masterCopyAddress === fractalAzoriusMasterCopy, + [fractalAzoriusMasterCopy], ); const isFractalModule = useCallback( (masterCopyAddress: Address) => masterCopyAddress === fractalModuleMasterCopy, diff --git a/src/hooks/utils/useVotingStrategyAddress.ts b/src/hooks/utils/useVotingStrategyAddress.ts new file mode 100644 index 0000000000..613587bd4f --- /dev/null +++ b/src/hooks/utils/useVotingStrategyAddress.ts @@ -0,0 +1,58 @@ +import { useCallback } from 'react'; +import { Address, getAddress, getContract } from 'viem'; +import { usePublicClient } from 'wagmi'; +import AzoriusAbi from '../../assets/abi/Azorius'; +import { SENTINEL_ADDRESS } from '../../constants/common'; +import { useFractal } from '../../providers/App/AppProvider'; +import { useSafeAPI } from '../../providers/App/hooks/useSafeAPI'; +import { FractalModuleData } from '../../types'; +import { getAzoriusModuleFromModules } from '../../utils'; +import { useFractalModules } from '../DAO/loaders/useFractalModules'; + +const useVotingStrategyAddress = () => { + const { node } = useFractal(); + const publicClient = usePublicClient(); + const safeAPI = useSafeAPI(); + const lookupModules = useFractalModules(); + + const getVotingStrategyAddress = useCallback( + async (safeAddress?: Address) => { + let azoriusModule: FractalModuleData | undefined; + + if (safeAddress) { + if (!safeAPI) { + throw new Error('Safe API not ready'); + } + const safeInfo = await safeAPI.getSafeInfo(getAddress(safeAddress)); + const safeModules = await lookupModules(safeInfo.modules); + azoriusModule = getAzoriusModuleFromModules(safeModules); + if (!azoriusModule) return; + } else { + azoriusModule = getAzoriusModuleFromModules(node.fractalModules); + } + + if (!azoriusModule) { + throw new Error('Azorius module not found'); + } + + if (!publicClient) { + throw new Error('Public client undefined'); + } + + const azoriusContract = getContract({ + abi: AzoriusAbi, + address: getAddress(azoriusModule.moduleAddress), + client: publicClient, + }); + + // @dev assumes the first strategy is the voting contract + const strategies = await azoriusContract.read.getStrategies([SENTINEL_ADDRESS, 0n]); + return strategies[1]; + }, + [lookupModules, node.fractalModules, publicClient, safeAPI], + ); + + return { getVotingStrategyAddress }; +}; + +export default useVotingStrategyAddress; diff --git a/src/models/AzoriusTxBuilder.ts b/src/models/AzoriusTxBuilder.ts index c73070d48d..e48ef34fc0 100644 --- a/src/models/AzoriusTxBuilder.ts +++ b/src/models/AzoriusTxBuilder.ts @@ -1,4 +1,3 @@ -import { Azorius, Azorius__factory } from '@fractal-framework/fractal-contracts'; import { getCreate2Address, Address, @@ -9,11 +8,11 @@ import { parseAbiParameters, getAddress, isAddress, - isHex, encodeFunctionData, PublicClient, getContract, } from 'viem'; +import AzoriusAbi from '../assets/abi/Azorius'; import ERC20ClaimAbi from '../assets/abi/ERC20Claim'; import GnosisSafeL2Abi from '../assets/abi/GnosisSafeL2'; import LinearERC20VotingAbi from '../assets/abi/LinearERC20Voting'; @@ -49,7 +48,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { private predictedAzoriusAddress: Address | undefined; private predictedTokenClaimAddress: Address | undefined; - public azoriusContract: Azorius | undefined; + public azoriusAddress: Address | undefined; public linearERC20VotingAddress: Address | undefined; public linearERC721VotingAddress: Address | undefined; public votesTokenAddress: Address | undefined; @@ -61,6 +60,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { private erc20ClaimMasterCopyAddress: Address; private linearERC20VotingMasterCopyAddress: Address; private linearERC721VotingMasterCopyAddress: Address; + private azoriusMasterCopyAddress: Address; private tokenNonce: bigint; private strategyNonce: bigint; @@ -81,6 +81,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { erc20ClaimMasterCopyAddress: Address, linearERC20VotingMasterCopyAddress: Address, linearERC721VotingMasterCopyAddress: Address, + azoriusMasterCopyAddress: Address, parentAddress?: Address, parentTokenAddress?: Address, ) { @@ -108,6 +109,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { this.erc20ClaimMasterCopyAddress = erc20ClaimMasterCopyAddress; this.linearERC20VotingMasterCopyAddress = linearERC20VotingMasterCopyAddress; this.linearERC721VotingMasterCopyAddress = linearERC721VotingMasterCopyAddress; + this.azoriusMasterCopyAddress = azoriusMasterCopyAddress; if (daoData.votingStrategyType === VotingStrategyType.LINEAR_ERC20) { daoData = daoData as AzoriusERC20DAO; @@ -168,7 +170,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { LinearERC20VotingAbi, this.linearERC20VotingAddress!, 'setAzorius', // contract function name - [this.azoriusContract!.address], + [this.azoriusAddress], 0, false, ); @@ -177,7 +179,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { LinearERC721VotingAbi, this.linearERC721VotingAddress!, 'setAzorius', // contract function name - [this.azoriusContract!.address], + [this.azoriusAddress], 0, false, ); @@ -191,7 +193,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { GnosisSafeL2Abi, this.safeContractAddress, 'enableModule', - [this.azoriusContract!.address], + [this.azoriusAddress], 0, false, ); @@ -202,7 +204,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { GnosisSafeL2Abi, this.safeContractAddress, 'addOwnerWithThreshold', - [this.azoriusContract!.address, 1], + [this.azoriusAddress, 1], 0, false, ); @@ -213,7 +215,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { GnosisSafeL2Abi, this.safeContractAddress, 'removeOwner', - [this.azoriusContract!.address, this.multiSendCallOnlyAddress, 1], + [this.azoriusAddress, this.multiSendCallOnlyAddress, 1], 0, false, ); @@ -254,11 +256,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { ModuleProxyFactoryAbi, this.moduleProxyFactoryAddress, 'deployModule', - [ - this.azoriusContracts!.fractalAzoriusMasterCopyContract.address, - this.encodedSetupAzoriusData, - this.azoriusNonce, - ], + [this.azoriusMasterCopyAddress, this.encodedSetupAzoriusData, this.azoriusNonce], 0, false, ); @@ -555,19 +553,13 @@ export class AzoriusTxBuilder extends BaseTxBuilder { ], ); - const encodedSetupAzoriusData = - this.azoriusContracts!.fractalAzoriusMasterCopyContract.interface.encodeFunctionData( - 'setUp', - [encodedInitAzoriusData], - ); - - if (!isHex(encodedSetupAzoriusData)) { - throw new Error('Error encoding setup azorius data'); - } + const encodedSetupAzoriusData = encodeFunctionData({ + abi: AzoriusAbi, + functionName: 'setUp', + args: [encodedInitAzoriusData], + }); - const azoriusByteCodeLinear = generateContractByteCodeLinear( - getAddress(this.azoriusContracts!.fractalAzoriusMasterCopyContract.address), - ); + const azoriusByteCodeLinear = generateContractByteCodeLinear(this.azoriusMasterCopyAddress); const azoriusSalt = generateSalt(encodedSetupAzoriusData, this.azoriusNonce); this.encodedSetupAzoriusData = encodedSetupAzoriusData; @@ -584,10 +576,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { } const daoData = this.daoData as AzoriusGovernanceDAO; - this.azoriusContract = Azorius__factory.connect( - this.predictedAzoriusAddress!, - this.signerOrProvider, - ); + this.azoriusAddress = this.predictedAzoriusAddress; if (daoData.votingStrategyType === VotingStrategyType.LINEAR_ERC20) { this.votesTokenAddress = this.predictedTokenAddress; this.linearERC20VotingAddress = this.predictedStrategyAddress; diff --git a/src/models/DaoTxBuilder.ts b/src/models/DaoTxBuilder.ts index f2bbc61c17..43b889530d 100644 --- a/src/models/DaoTxBuilder.ts +++ b/src/models/DaoTxBuilder.ts @@ -1,5 +1,6 @@ import { ethers } from 'ethers'; import { Address, PublicClient, encodeFunctionData, getAddress, zeroAddress } from 'viem'; +import AzoriusAbi from '../assets/abi/Azorius'; import FractalRegistryAbi from '../assets/abi/FractalRegistry'; import GnosisSafeL2Abi from '../assets/abi/GnosisSafeL2'; import KeyValuePairsAbi from '../assets/abi/KeyValuePairs'; @@ -114,7 +115,7 @@ export class DaoTxBuilder extends BaseTxBuilder { if (this.parentAddress) { const freezeGuardTxBuilder = this.txBuilderFactory.createFreezeGuardTxBuilder( - azoriusTxBuilder.azoriusContract!.address, + azoriusTxBuilder.azoriusAddress, azoriusTxBuilder.linearERC20VotingAddress ?? azoriusTxBuilder.linearERC721VotingAddress, this.parentStrategyType, this.parentStrategyAddress, @@ -126,7 +127,7 @@ export class DaoTxBuilder extends BaseTxBuilder { freezeGuardTxBuilder.buildDeployZodiacModuleTx(), freezeGuardTxBuilder.buildFreezeVotingSetupTx(), freezeGuardTxBuilder.buildDeployFreezeGuardTx(), - freezeGuardTxBuilder.buildSetGuardTx(azoriusTxBuilder.azoriusContract!), + freezeGuardTxBuilder.buildSetGuardTx(AzoriusAbi, azoriusTxBuilder.azoriusAddress!), ]); } const data = this.daoData as AzoriusERC20DAO; diff --git a/src/models/FreezeGuardTxBuilder.ts b/src/models/FreezeGuardTxBuilder.ts index 7a70c767a6..8027aefd8d 100644 --- a/src/models/FreezeGuardTxBuilder.ts +++ b/src/models/FreezeGuardTxBuilder.ts @@ -1,5 +1,4 @@ import { - Azorius, AzoriusFreezeGuard__factory, ERC20FreezeVoting__factory, MultisigFreezeGuard__factory, @@ -20,6 +19,7 @@ import { parseAbiParameters, isHex, PublicClient, + Abi, } from 'viem'; import GnosisSafeL2Abi from '../assets/abi/GnosisSafeL2'; import ModuleProxyFactoryAbi from '../assets/abi/ModuleProxyFactory'; @@ -151,8 +151,8 @@ export class FreezeGuardTxBuilder extends BaseTxBuilder { ); } - public buildSetGuardTx(contract: Azorius): SafeTransaction { - return buildContractCall(contract, 'setGuard', [this.freezeGuardAddress], 0, false); + public buildSetGuardTx(abi: Abi, address: Address): SafeTransaction { + return buildContractCallViem(abi, address, 'setGuard', [this.freezeGuardAddress], 0, false); } public buildSetGuardTxSafe(safeAddress: Address): SafeTransaction { diff --git a/src/models/TxBuilderFactory.ts b/src/models/TxBuilderFactory.ts index 5fa528cd23..31a0826a0e 100644 --- a/src/models/TxBuilderFactory.ts +++ b/src/models/TxBuilderFactory.ts @@ -41,6 +41,7 @@ export class TxBuilderFactory extends BaseTxBuilder { private fractalModuleMasterCopyAddress: string; private linearERC20VotingMasterCopyAddress: string; private linearERC721VotingMasterCopyAddress: string; + private azoriusMasterCopyAddress: string; constructor( signerOrProvider: ethers.Signer | any, @@ -61,6 +62,7 @@ export class TxBuilderFactory extends BaseTxBuilder { fractalModuleMasterCopyAddress: string, linearERC20VotingMasterCopyAddress: string, linearERC721VotingMasterCopyAddress: string, + azoriusMasterCopyAddress: string, parentAddress?: string, parentTokenAddress?: string, ) { @@ -88,6 +90,7 @@ export class TxBuilderFactory extends BaseTxBuilder { this.fractalModuleMasterCopyAddress = fractalModuleMasterCopyAddress; this.linearERC20VotingMasterCopyAddress = linearERC20VotingMasterCopyAddress; this.linearERC721VotingMasterCopyAddress = linearERC721VotingMasterCopyAddress; + this.azoriusMasterCopyAddress = azoriusMasterCopyAddress; } public setSafeContract(safeAddress: Address) { @@ -194,6 +197,7 @@ export class TxBuilderFactory extends BaseTxBuilder { getAddress(this.erc20ClaimMasterCopyAddress), getAddress(this.linearERC20VotingMasterCopyAddress), getAddress(this.linearERC721VotingMasterCopyAddress), + getAddress(this.azoriusMasterCopyAddress), this.parentAddress ? getAddress(this.parentAddress) : undefined, this.parentTokenAddress ? getAddress(this.parentTokenAddress) : undefined, ); diff --git a/src/providers/App/governanceContracts/action.ts b/src/providers/App/governanceContracts/action.ts index c698710023..401f7dc83d 100644 --- a/src/providers/App/governanceContracts/action.ts +++ b/src/providers/App/governanceContracts/action.ts @@ -1,10 +1,10 @@ import { FractalGovernanceContracts } from '../../../types'; export enum GovernanceContractAction { - SET_GOVERNANCE_CONTRACT = 'SET_GOVERNANCE_CONTRACT', + SET_GOVERNANCE_CONTRACT_ADDRESSES = 'SET_GOVERNANCE_CONTRACT_ADDRESSES', } export type GovernanceContractActions = { - type: GovernanceContractAction.SET_GOVERNANCE_CONTRACT; + type: GovernanceContractAction.SET_GOVERNANCE_CONTRACT_ADDRESSES; payload: Omit; }; diff --git a/src/providers/App/governanceContracts/reducer.ts b/src/providers/App/governanceContracts/reducer.ts index 069f80200f..2a8fc1f14d 100644 --- a/src/providers/App/governanceContracts/reducer.ts +++ b/src/providers/App/governanceContracts/reducer.ts @@ -15,7 +15,7 @@ export const governanceContractsReducer = ( action: GovernanceContractActions, ) => { switch (action.type) { - case GovernanceContractAction.SET_GOVERNANCE_CONTRACT: + case GovernanceContractAction.SET_GOVERNANCE_CONTRACT_ADDRESSES: return { ...action.payload, isLoaded: true }; default: return state; diff --git a/src/providers/NetworkConfig/networks/mainnet.ts b/src/providers/NetworkConfig/networks/mainnet.ts index eb82fd81d4..9f7e8ebd97 100644 --- a/src/providers/NetworkConfig/networks/mainnet.ts +++ b/src/providers/NetworkConfig/networks/mainnet.ts @@ -19,7 +19,7 @@ import { getSafeL2SingletonDeployment, getCompatibilityFallbackHandlerDeployment, } from '@safe-global/safe-deployments'; -import { getAddress, zeroAddress } from 'viem'; +import { getAddress } from 'viem'; import { mainnet } from 'wagmi/chains'; import { GovernanceType } from '../../../types'; import { NetworkConfig } from '../../../types/network'; diff --git a/src/providers/NetworkConfig/networks/polygon.ts b/src/providers/NetworkConfig/networks/polygon.ts index fd7be0e04d..6ee358d8d2 100644 --- a/src/providers/NetworkConfig/networks/polygon.ts +++ b/src/providers/NetworkConfig/networks/polygon.ts @@ -19,7 +19,7 @@ import { getSafeL2SingletonDeployment, getCompatibilityFallbackHandlerDeployment, } from '@safe-global/safe-deployments'; -import { getAddress, zeroAddress } from 'viem'; +import { getAddress } from 'viem'; import { polygon } from 'wagmi/chains'; import { GovernanceType } from '../../../types'; import { NetworkConfig } from '../../../types/network'; diff --git a/src/types/fractal.ts b/src/types/fractal.ts index 7188e4272d..621d7f114c 100644 --- a/src/types/fractal.ts +++ b/src/types/fractal.ts @@ -1,5 +1,4 @@ import { - Azorius, AzoriusFreezeGuard, ERC20FreezeVoting, MultisigFreezeVoting, @@ -14,8 +13,7 @@ import { SafeCollectibleResponse, } from '@safe-global/safe-service-client'; import { Dispatch } from 'react'; -import { Address, GetContractReturnType, PublicClient } from 'viem'; -import FractalModuleAbi from '../assets/abi/FractalModule'; +import { Address } from 'viem'; import { FractalGovernanceActions } from '../providers/App/governance/action'; import { GovernanceContractActions } from '../providers/App/governanceContracts/action'; import { FractalGuardActions } from '../providers/App/guard/action'; @@ -234,10 +232,6 @@ export interface Node extends Omit {} export interface FractalModuleData { - moduleContract: - | Azorius - | GetContractReturnType - | undefined; moduleAddress: string; moduleType: FractalModuleType; } @@ -318,7 +312,6 @@ export interface NodeHierarchy { } export interface FractalContracts { - fractalAzoriusMasterCopyContract: ContractConnection; multisigFreezeGuardMasterCopyContract: ContractConnection; azoriusFreezeGuardMasterCopyContract: ContractConnection; freezeMultisigVotingMasterCopyContract: ContractConnection; diff --git a/src/types/strategyAzorius.ts b/src/types/strategyAzorius.ts index a81fe8b6c8..5d695add40 100644 --- a/src/types/strategyAzorius.ts +++ b/src/types/strategyAzorius.ts @@ -1,6 +1,5 @@ -import { Azorius, AzoriusFreezeGuard } from '@fractal-framework/fractal-contracts'; +import { AzoriusFreezeGuard } from '@fractal-framework/fractal-contracts'; export interface AzoriusContracts { - fractalAzoriusMasterCopyContract: Azorius; azoriusFreezeGuardMasterCopyContract: AzoriusFreezeGuard; } diff --git a/src/utils/azorius.ts b/src/utils/azorius.ts index b10cf0b4cd..bce7bf890f 100644 --- a/src/utils/azorius.ts +++ b/src/utils/azorius.ts @@ -1,7 +1,6 @@ -import { Azorius } from '@fractal-framework/fractal-contracts'; -import { ProposalExecutedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/Azorius'; import { SafeMultisigTransactionWithTransfersResponse } from '@safe-global/safe-service-client'; import { GetContractEventsReturnType, GetContractReturnType, PublicClient } from 'viem'; +import AzoriusAbi from '../assets/abi/Azorius'; import LinearERC20VotingAbi from '../assets/abi/LinearERC20Voting'; import LinearERC721VotingAbi from '../assets/abi/LinearERC721Voting'; import { strategyFractalProposalStates } from '../constants/strategy'; @@ -28,10 +27,10 @@ import { Providers } from '../types/network'; import { getTimeStamp } from './contract'; export const getAzoriusProposalState = async ( - azoriusContract: Azorius, - proposalId: bigint, + azoriusContract: GetContractReturnType, + proposalId: number, ): Promise => { - const state = await azoriusContract.proposalState(proposalId); + const state = await azoriusContract.read.proposalState([proposalId]); return strategyFractalProposalStates[state]; }; @@ -43,12 +42,12 @@ const getQuorum = async ( | GetContractReturnType | undefined, strategyType: VotingStrategyType, - proposalId: bigint, + proposalId: number, ) => { let quorum; if (strategyType === VotingStrategyType.LINEAR_ERC20 && erc20StrategyContract) { - quorum = await erc20StrategyContract.read.quorumVotes([Number(proposalId)]); + quorum = await erc20StrategyContract.read.quorumVotes([proposalId]); } else if (strategyType === VotingStrategyType.LINEAR_ERC721 && erc721StrategyContract) { quorum = await erc721StrategyContract.read.quorumThreshold(); } else { @@ -62,7 +61,7 @@ export const getProposalVotesSummary = async ( erc20Strategy: GetContractReturnType | undefined, erc721Strategy: GetContractReturnType | undefined, strategyType: VotingStrategyType, - proposalId: bigint, + proposalId: number, ): Promise => { try { if (erc20Strategy !== undefined && erc721Strategy !== undefined) { @@ -71,7 +70,7 @@ export const getProposalVotesSummary = async ( if (erc20Strategy !== undefined) { const [noVotes, yesVotes, abstainVotes] = await erc20Strategy.read.getProposalVotes([ - Number(proposalId), + proposalId, ]); return { @@ -82,7 +81,7 @@ export const getProposalVotesSummary = async ( }; } else if (erc721Strategy !== undefined) { const [noVotes, yesVotes, abstainVotes] = await erc721Strategy.read.getProposalVotes([ - Number(proposalId), + proposalId, ]); return { @@ -115,7 +114,7 @@ export const getProposalVotesSummary = async ( const getProposalVotes = ( erc20VotedEvents: GetContractEventsReturnType | undefined, erc721VotedEvents: GetContractEventsReturnType | undefined, - proposalId: bigint, + proposalId: number, ): (ProposalVote | ERC721ProposalVote)[] => { if (erc20VotedEvents !== undefined && erc721VotedEvents !== undefined) { throw new Error("two voting contracts? we don't support that."); @@ -124,7 +123,7 @@ const getProposalVotes = ( if (erc20VotedEvents !== undefined) { const erc20ProposalVoteEvents = erc20VotedEvents.filter(voteEvent => { if (!voteEvent.args.proposalId) return false; - return proposalId === BigInt(voteEvent.args.proposalId); + return proposalId === voteEvent.args.proposalId; }); const events = []; @@ -142,7 +141,7 @@ const getProposalVotes = ( } else if (erc721VotedEvents !== undefined) { const erc721ProposalVoteEvents = erc721VotedEvents.filter(voteEvent => { if (!voteEvent.args.proposalId) return false; - return proposalId === BigInt(voteEvent.args.proposalId); + return proposalId === voteEvent.args.proposalId; }); const events = []; @@ -171,17 +170,13 @@ export const mapProposalCreatedEventToProposal = async ( | GetContractReturnType | undefined, strategyType: VotingStrategyType, - proposalId: bigint, + proposalId: number, proposer: string, - azoriusContract: Azorius, + azoriusContract: GetContractReturnType, provider: Providers, - erc20VotedEvents: Promise< - GetContractEventsReturnType | undefined - >, - erc721VotedEvents: Promise< - GetContractEventsReturnType | undefined - >, - executedEvents: Promise, + erc20VotedEvents: GetContractEventsReturnType | undefined, + erc721VotedEvents: GetContractEventsReturnType | undefined, + executedEvents: GetContractEventsReturnType | undefined, data?: ProposalData, ) => { if (erc20StrategyContract !== undefined && erc721StrategyContract !== undefined) { @@ -197,18 +192,14 @@ export const mapProposalCreatedEventToProposal = async ( }; if (erc20StrategyContract !== undefined) { - const stratProposalVotes = await erc20StrategyContract.read.getProposalVotes([ - Number(proposalId), - ]); + const stratProposalVotes = await erc20StrategyContract.read.getProposalVotes([proposalId]); proposalVotes.noVotes = stratProposalVotes[0]; proposalVotes.yesVotes = stratProposalVotes[1]; proposalVotes.abstainVotes = stratProposalVotes[2]; proposalVotes.startBlock = stratProposalVotes[3]; proposalVotes.endBlock = stratProposalVotes[4]; } else if (erc721StrategyContract !== undefined) { - const stratProposalVotes = await erc721StrategyContract.read.getProposalVotes([ - Number(proposalId), - ]); + const stratProposalVotes = await erc721StrategyContract.read.getProposalVotes([proposalId]); proposalVotes.noVotes = stratProposalVotes[0]; proposalVotes.yesVotes = stratProposalVotes[1]; proposalVotes.abstainVotes = stratProposalVotes[2]; @@ -243,7 +234,7 @@ export const mapProposalCreatedEventToProposal = async ( let transactionHash: string | undefined; if (state === FractalProposalState.EXECUTED) { const executedEvent = (await executedEvents)?.find( - event => BigInt(event.args[0]) === proposalId, + event => event.args.proposalId === proposalId, ); transactionHash = executedEvent?.transactionHash; }