diff --git a/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts b/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts index 6fb71861bf..6a07e38970 100644 --- a/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts +++ b/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts @@ -1,14 +1,15 @@ -import { LinearERC20Voting, LinearERC721Voting } from '@fractal-framework/fractal-contracts'; +import { LinearERC721Voting } from '@fractal-framework/fractal-contracts'; 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 { VotedEvent as ERC20VotedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/LinearERC20Voting'; import { VotedEvent as ERC721VotedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/LinearERC721Voting'; import { Dispatch, useEffect, useMemo } from 'react'; -import { getAddress, Hex } from 'viem'; +import { getAddress, getContract, GetContractReturnType, Hex, PublicClient } from 'viem'; +import { usePublicClient } from 'wagmi'; +import LinearERC20VotingAbi from '../../../../assets/abi/LinearERC20Voting'; import { useFractal } from '../../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; import { useEthersProvider } from '../../../../providers/Ethers/hooks/useEthersProvider'; @@ -30,7 +31,9 @@ import { useSafeDecoder } from '../../../utils/useSafeDecoder'; const proposalCreatedEventListener = ( azoriusContract: Azorius, - erc20StrategyContract: LinearERC20Voting | undefined, + erc20StrategyContract: + | GetContractReturnType + | undefined, erc721StrategyContract: LinearERC721Voting | undefined, provider: Providers, strategyType: VotingStrategyType, @@ -88,32 +91,6 @@ const proposalCreatedEventListener = ( }; }; -const erc20VotedEventListener = ( - erc20StrategyContract: LinearERC20Voting, - strategyType: VotingStrategyType, - dispatch: Dispatch, -): TypedListener => { - return async (voter, proposalId, voteType, weight) => { - const votesSummary = await getProposalVotesSummary( - erc20StrategyContract, - undefined, - strategyType, - BigInt(proposalId), - ); - - dispatch({ - type: FractalGovernanceAction.UPDATE_NEW_AZORIUS_ERC20_VOTE, - payload: { - proposalId: proposalId.toString(), - voter, - support: voteType, - weight: weight.toBigInt(), - votesSummary, - }, - }); - }; -}; - const erc721VotedEventListener = ( erc721StrategyContract: LinearERC721Voting, strategyType: VotingStrategyType, @@ -155,6 +132,8 @@ export const useAzoriusListeners = () => { const provider = useEthersProvider(); const decode = useSafeDecoder(); + const publicClient = usePublicClient(); + const azoriusContract = useMemo(() => { if (!baseContracts || !azoriusContractAddress) { return; @@ -174,14 +153,16 @@ export const useAzoriusListeners = () => { }, [ozLinearVotingContractAddress, erc721LinearVotingContractAddress]); const erc20StrategyContract = useMemo(() => { - if (!baseContracts || !ozLinearVotingContractAddress) { + if (!ozLinearVotingContractAddress || !publicClient) { return undefined; } - return baseContracts.linearVotingMasterCopyContract.asProvider.attach( - ozLinearVotingContractAddress, - ); - }, [baseContracts, ozLinearVotingContractAddress]); + return getContract({ + abi: LinearERC20VotingAbi, + address: getAddress(ozLinearVotingContractAddress), + client: publicClient, + }); + }, [ozLinearVotingContractAddress, publicClient]); const erc721StrategyContract = useMemo(() => { if (!baseContracts || !erc721LinearVotingContractAddress) { @@ -229,15 +210,38 @@ export const useAzoriusListeners = () => { return; } - const votedEvent = erc20StrategyContract.filters.Voted(); - const listener = erc20VotedEventListener(erc20StrategyContract, strategyType, action.dispatch); - - erc20StrategyContract.on(votedEvent, listener); + const unwatch = erc20StrategyContract.watchEvent.Voted({ + onLogs: async logs => { + for (const log of logs) { + if (!log.args.proposalId || !log.args.voter || !log.args.voteType || !log.args.weight) { + continue; + } + + const votesSummary = await getProposalVotesSummary( + erc20StrategyContract, + undefined, + strategyType, + BigInt(log.args.proposalId), + ); + + action.dispatch({ + type: FractalGovernanceAction.UPDATE_NEW_AZORIUS_ERC20_VOTE, + payload: { + proposalId: log.args.proposalId.toString(), + voter: log.args.voter, + support: log.args.voteType, + weight: log.args.weight, + votesSummary, + }, + }); + } + }, + }); return () => { - erc20StrategyContract.off(votedEvent, listener); + unwatch(); }; - }, [action.dispatch, erc20StrategyContract, strategyType]); + }, [action, erc20StrategyContract, strategyType]); useEffect(() => { if (strategyType !== VotingStrategyType.LINEAR_ERC721 || !erc721StrategyContract) { diff --git a/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts b/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts index 56954fbabb..ad2c8c2289 100644 --- a/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts +++ b/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts @@ -1,12 +1,20 @@ -import { LinearERC20Voting, LinearERC721Voting } from '@fractal-framework/fractal-contracts'; +import { LinearERC721Voting } from '@fractal-framework/fractal-contracts'; import { Azorius, ProposalExecutedEvent, } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/Azorius'; -import { VotedEvent as ERC20VotedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/LinearERC20Voting'; import { VotedEvent as ERC721VotedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/LinearERC721Voting'; import { useCallback, useEffect, useMemo, useRef } from 'react'; -import { Hex, getAddress } from 'viem'; +import { + GetContractEventsReturnType, + GetContractReturnType, + Hex, + PublicClient, + getAddress, + getContract, +} from 'viem'; +import { usePublicClient } from 'wagmi'; +import LinearERC20VotingAbi from '../../../../assets/abi/LinearERC20Voting'; import { logError } from '../../../../helpers/errorLogging'; import { useFractal } from '../../../../providers/App/AppProvider'; import { useEthersProvider } from '../../../../providers/Ethers/hooks/useEthersProvider'; @@ -32,6 +40,8 @@ export const useAzoriusProposals = () => { const provider = useEthersProvider(); const decode = useSafeDecoder(); + const publicClient = usePublicClient(); + const azoriusContract = useMemo(() => { if (!baseContracts || !azoriusContractAddress) { return; @@ -51,14 +61,16 @@ export const useAzoriusProposals = () => { }, [ozLinearVotingContractAddress, erc721LinearVotingContractAddress]); const erc20StrategyContract = useMemo(() => { - if (!baseContracts || !ozLinearVotingContractAddress) { + if (!ozLinearVotingContractAddress || !publicClient) { return undefined; } - return baseContracts.linearVotingMasterCopyContract.asProvider.attach( - ozLinearVotingContractAddress, - ); - }, [baseContracts, ozLinearVotingContractAddress]); + return getContract({ + abi: LinearERC20VotingAbi, + address: getAddress(ozLinearVotingContractAddress), + client: publicClient, + }); + }, [ozLinearVotingContractAddress, publicClient]); const erc721StrategyContract = useMemo(() => { if (!baseContracts || !erc721LinearVotingContractAddress) { @@ -75,10 +87,7 @@ export const useAzoriusProposals = () => { return; } - const filter = erc20StrategyContract.filters.Voted(); - const events = await erc20StrategyContract.queryFilter(filter); - - return events; + return erc20StrategyContract.getEvents.Voted(); }, [erc20StrategyContract]); const erc721VotedEvents = useMemo(async () => { @@ -116,10 +125,14 @@ export const useAzoriusProposals = () => { const loadAzoriusProposals = useCallback( async ( _azoriusContract: Azorius | undefined, - _erc20StrategyContract: LinearERC20Voting | undefined, + _erc20StrategyContract: + | GetContractReturnType + | undefined, _erc721StrategyContract: LinearERC721Voting | undefined, _strategyType: VotingStrategyType | undefined, - _erc20VotedEvents: Promise, + _erc20VotedEvents: Promise< + GetContractEventsReturnType | undefined + >, _erc721VotedEvents: Promise, _executedEvents: Promise, _provider: Providers | undefined, diff --git a/src/hooks/DAO/loaders/governance/useERC20LinearStrategy.ts b/src/hooks/DAO/loaders/governance/useERC20LinearStrategy.ts index 9b49822822..304b3aecfc 100644 --- a/src/hooks/DAO/loaders/governance/useERC20LinearStrategy.ts +++ b/src/hooks/DAO/loaders/governance/useERC20LinearStrategy.ts @@ -1,7 +1,7 @@ -import { TypedListener } from '@fractal-framework/fractal-contracts/dist/typechain-types/common'; -import { QuorumNumeratorUpdatedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/BaseQuorumPercent'; -import { VotingPeriodUpdatedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/LinearERC20Voting'; -import { useCallback, useEffect } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; +import { getAddress, getContract } from 'viem'; +import { usePublicClient } from 'wagmi'; +import LinearERC20VotingAbi from '../../../../assets/abi/LinearERC20Voting'; import { useFractal } from '../../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; import { useEthersProvider } from '../../../../providers/Ethers/hooks/useEthersProvider'; @@ -18,21 +18,32 @@ export const useERC20LinearStrategy = () => { const baseContracts = useSafeContracts(); const provider = useEthersProvider(); const { getTimeDuration } = useTimeHelpers(); + const publicClient = usePublicClient(); + + const ozLinearVotingContract = useMemo(() => { + if (!ozLinearVotingContractAddress || !publicClient) { + return; + } + + return getContract({ + abi: LinearERC20VotingAbi, + address: getAddress(ozLinearVotingContractAddress), + client: publicClient, + }); + }, [ozLinearVotingContractAddress, publicClient]); const loadERC20Strategy = useCallback(async () => { - if (!ozLinearVotingContractAddress || !azoriusContractAddress || !provider || !baseContracts) { + if (!ozLinearVotingContract || !azoriusContractAddress || !provider || !baseContracts) { return {}; } - const ozLinearVotingContract = baseContracts.linearVotingMasterCopyContract.asProvider.attach( - ozLinearVotingContractAddress, - ); + const azoriusContract = baseContracts.fractalAzoriusMasterCopyContract.asProvider.attach(azoriusContractAddress); const [votingPeriodBlocks, quorumNumerator, quorumDenominator, timeLockPeriod] = await Promise.all([ - ozLinearVotingContract.votingPeriod(), - (await ozLinearVotingContract.quorumNumerator()).toBigInt(), - (await ozLinearVotingContract.QUORUM_DENOMINATOR()).toBigInt(), + ozLinearVotingContract.read.votingPeriod(), + ozLinearVotingContract.read.quorumNumerator(), + ozLinearVotingContract.read.QUORUM_DENOMINATOR(), azoriusContract.timelockPeriod(), ]); @@ -56,56 +67,57 @@ export const useERC20LinearStrategy = () => { }; action.dispatch({ type: FractalGovernanceAction.SET_STRATEGY, payload: votingData }); }, [ - ozLinearVotingContractAddress, + action, azoriusContractAddress, + baseContracts, getTimeDuration, - action, + ozLinearVotingContract, provider, - baseContracts, ]); useEffect(() => { - if (!ozLinearVotingContractAddress || !baseContracts) { + if (!ozLinearVotingContract || !publicClient) { return; } - const ozLinearVotingContract = baseContracts.linearVotingMasterCopyContract.asProvider.attach( - ozLinearVotingContractAddress, - ); - const votingPeriodfilter = ozLinearVotingContract.filters.VotingPeriodUpdated(); - const listener: TypedListener = votingPeriod => { - action.dispatch({ - type: FractalGovernanceAction.UPDATE_VOTING_PERIOD, - payload: BigInt(votingPeriod), - }); - }; - ozLinearVotingContract.on(votingPeriodfilter, listener); + const unwatch = ozLinearVotingContract.watchEvent.VotingPeriodUpdated({ + onLogs: logs => { + const lastLog = logs.pop(); + if (lastLog && lastLog.args.votingPeriod) { + action.dispatch({ + type: FractalGovernanceAction.UPDATE_VOTING_PERIOD, + payload: BigInt(lastLog.args.votingPeriod), + }); + } + }, + }); + return () => { - ozLinearVotingContract.off(votingPeriodfilter, listener); + unwatch(); }; - }, [ozLinearVotingContractAddress, action, baseContracts]); + }, [action, ozLinearVotingContract, publicClient]); useEffect(() => { - if (!ozLinearVotingContractAddress || !baseContracts) { + if (!ozLinearVotingContract) { return; } - const ozLinearVotingContract = baseContracts.linearVotingMasterCopyContract.asProvider.attach( - ozLinearVotingContractAddress, - ); - const quorumNumeratorUpdatedFilter = ozLinearVotingContract.filters.QuorumNumeratorUpdated(); - const quorumNumeratorUpdatedListener: TypedListener< - QuorumNumeratorUpdatedEvent - > = quorumPercentage => { - action.dispatch({ - type: FractalGovernanceAction.UPDATE_VOTING_QUORUM, - payload: quorumPercentage.toBigInt(), - }); - }; - ozLinearVotingContract.on(quorumNumeratorUpdatedFilter, quorumNumeratorUpdatedListener); + + const unwatch = ozLinearVotingContract.watchEvent.QuorumNumeratorUpdated({ + onLogs: logs => { + const lastLog = logs.pop(); + if (lastLog && lastLog.args.quorumNumerator) { + action.dispatch({ + type: FractalGovernanceAction.UPDATE_VOTING_QUORUM, + payload: lastLog.args.quorumNumerator, + }); + } + }, + }); + return () => { - ozLinearVotingContract.off(quorumNumeratorUpdatedFilter, quorumNumeratorUpdatedListener); + unwatch(); }; - }, [ozLinearVotingContractAddress, action, baseContracts]); + }, [action, ozLinearVotingContract]); return loadERC20Strategy; }; diff --git a/src/hooks/DAO/loaders/useGovernanceContracts.ts b/src/hooks/DAO/loaders/useGovernanceContracts.ts index 50a9170130..5fc4c4df79 100644 --- a/src/hooks/DAO/loaders/useGovernanceContracts.ts +++ b/src/hooks/DAO/loaders/useGovernanceContracts.ts @@ -2,6 +2,7 @@ import { Azorius } from '@fractal-framework/fractal-contracts'; import { useCallback, useEffect, useRef } from 'react'; import { getContract, getAddress } 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'; @@ -25,7 +26,7 @@ export const useGovernanceContracts = () => { if (!baseContracts || !publicClient) { return; } - const { fractalAzoriusMasterCopyContract, linearVotingMasterCopyContract } = baseContracts; + const { fractalAzoriusMasterCopyContract } = baseContracts; const azoriusModule = getAzoriusModuleFromModules(fractalModules); const azoriusModuleContract = azoriusModule?.moduleContract as Azorius; @@ -33,9 +34,6 @@ export const useGovernanceContracts = () => { const azoriusContract = fractalAzoriusMasterCopyContract.asProvider.attach( azoriusModuleContract.address, ); - - let govTokenAddress: string | undefined; - let ozLinearVotingContractAddress: string | undefined; let erc721LinearVotingContractAddress: string | undefined; let votesTokenContractAddress: string | undefined; @@ -53,11 +51,13 @@ export const useGovernanceContracts = () => { if (isOzLinearVoting) { ozLinearVotingContractAddress = votingStrategyAddress; - // asProvider: linearVotingMasterCopyContract.asProvider.attach(votingStrategyAddress!), - const ozLinearVotingContract = linearVotingMasterCopyContract.asProvider.attach( - ozLinearVotingContractAddress, - ); - govTokenAddress = await ozLinearVotingContract.governanceToken(); + + const ozLinearVotingContract = getContract({ + abi: LinearERC20VotingAbi, + address: getAddress(ozLinearVotingContractAddress), + client: publicClient, + }); + const govTokenAddress = await ozLinearVotingContract.read.governanceToken(); const possibleERC20Wrapper = getContract({ abi: VotesERC20WrapperAbi, diff --git a/src/hooks/DAO/proposal/useCastVote.ts b/src/hooks/DAO/proposal/useCastVote.ts index 9a75f048b2..f43c38dcba 100644 --- a/src/hooks/DAO/proposal/useCastVote.ts +++ b/src/hooks/DAO/proposal/useCastVote.ts @@ -3,6 +3,9 @@ import { ethers } from 'ethers'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { toast } from 'react-toastify'; +import { getAddress, getContract } from 'viem'; +import { useWalletClient } from 'wagmi'; +import LinearERC20VotingAbi from '../../../assets/abi/LinearERC20Voting'; import { useVoteContext } from '../../../components/Proposals/ProposalVotes/context/VoteContext'; import { logError } from '../../../helpers/errorLogging'; import { useFractal } from '../../../providers/App/AppProvider'; @@ -52,12 +55,13 @@ const useCastVote = ({ const azoriusGovernance = useMemo(() => governance as AzoriusGovernance, [governance]); const { type } = azoriusGovernance; - const [contractCallCastVote, contractCallPending] = useTransaction(); + const [contractCallCastVote, contractCallPending, contractCallCastVoteViem] = useTransaction(); const { remainingTokenIds, remainingTokenAddresses } = useUserERC721VotingTokens( proposal.proposalId, ); const { getCanVote, getHasVoted } = useVoteContext(); + const { data: walletClient } = useWalletClient(); useEffect(() => { if (setPending) { @@ -85,12 +89,24 @@ const useCastVote = ({ const castVote = useCallback( async (vote: number) => { - let contractFn; - if (type === GovernanceType.AZORIUS_ERC20 && ozLinearVotingContractAddress && baseContracts) { - const ozLinearVotingContract = baseContracts.linearVotingMasterCopyContract.asSigner.attach( - ozLinearVotingContractAddress, - ); - contractFn = () => ozLinearVotingContract.vote(proposal.proposalId, vote); + if (type === GovernanceType.AZORIUS_ERC20 && ozLinearVotingContractAddress && walletClient) { + const ozLinearVotingContract = getContract({ + abi: LinearERC20VotingAbi, + address: getAddress(ozLinearVotingContractAddress), + client: walletClient, + }); + contractCallCastVoteViem({ + contractFn: () => ozLinearVotingContract.write.vote([Number(proposal.proposalId), vote]), + pendingMessage: t('pendingCastVote'), + failedMessage: t('failedCastVote'), + successMessage: t('successCastVote'), + successCallback: () => { + setTimeout(() => { + getHasVoted(); + getCanVote(true); + }, 3000); + }, + }); } else if ( type === GovernanceType.AZORIUS_ERC721 && erc721LinearVotingContractAddress && @@ -100,19 +116,14 @@ const useCastVote = ({ baseContracts.linearVotingERC721MasterCopyContract.asSigner.attach( erc721LinearVotingContractAddress, ); - - contractFn = () => - erc721LinearVotingContract.vote( - proposal.proposalId, - vote, - remainingTokenAddresses, - remainingTokenIds, - ); - } - - if (contractFn) { contractCallCastVote({ - contractFn, + contractFn: () => + erc721LinearVotingContract.vote( + proposal.proposalId, + vote, + remainingTokenAddresses, + remainingTokenIds, + ), pendingMessage: t('pendingCastVote'), failedMessage: t('failedCastVote'), successMessage: t('successCastVote'), @@ -126,17 +137,19 @@ const useCastVote = ({ } }, [ + baseContracts, contractCallCastVote, - t, - ozLinearVotingContractAddress, + contractCallCastVoteViem, erc721LinearVotingContractAddress, - type, - proposal, - remainingTokenAddresses, - remainingTokenIds, getCanVote, getHasVoted, - baseContracts, + ozLinearVotingContractAddress, + proposal.proposalId, + remainingTokenAddresses, + remainingTokenIds, + t, + type, + walletClient, ], ); diff --git a/src/hooks/DAO/useBuildDAOTx.ts b/src/hooks/DAO/useBuildDAOTx.ts index dded6beea6..d40ab0721e 100644 --- a/src/hooks/DAO/useBuildDAOTx.ts +++ b/src/hooks/DAO/useBuildDAOTx.ts @@ -31,6 +31,7 @@ const useBuildDAOTx = () => { multisend: multiSendCallOnly, claimingMasterCopy: erc20ClaimMasterCopy, fractalModuleMasterCopy, + linearVotingMasterCopy: linearERC20VotingMasterCopy, }, } = useNetworkConfig(); @@ -54,7 +55,6 @@ const useBuildDAOTx = () => { return; } const { - linearVotingMasterCopyContract, linearVotingERC721MasterCopyContract, fractalAzoriusMasterCopyContract, fractalModuleMasterCopyContract, @@ -75,17 +75,12 @@ const useBuildDAOTx = () => { daoData.governance === GovernanceType.AZORIUS_ERC20 || daoData.governance === GovernanceType.AZORIUS_ERC721 ) { - if ( - !fractalAzoriusMasterCopyContract || - !linearVotingMasterCopyContract || - !azoriusFreezeGuardMasterCopyContract - ) { + if (!fractalAzoriusMasterCopyContract || !azoriusFreezeGuardMasterCopyContract) { return; } azoriusContracts = { fractalAzoriusMasterCopyContract: fractalAzoriusMasterCopyContract.asSigner, - linearVotingMasterCopyContract: linearVotingMasterCopyContract.asSigner, linearVotingERC721MasterCopyContract: linearVotingERC721MasterCopyContract.asSigner, azoriusFreezeGuardMasterCopyContract: azoriusFreezeGuardMasterCopyContract.asSigner, }; @@ -116,6 +111,7 @@ const useBuildDAOTx = () => { multiSendCallOnly, erc20ClaimMasterCopy, fractalModuleMasterCopy, + linearERC20VotingMasterCopy, parentAddress, parentTokenAddress, ); @@ -173,6 +169,7 @@ const useBuildDAOTx = () => { multiSendCallOnly, erc20ClaimMasterCopy, fractalModuleMasterCopy, + linearERC20VotingMasterCopy, ], ); diff --git a/src/hooks/DAO/useDeployAzorius.ts b/src/hooks/DAO/useDeployAzorius.ts index 8640dfdf7b..c67317aac2 100644 --- a/src/hooks/DAO/useDeployAzorius.ts +++ b/src/hooks/DAO/useDeployAzorius.ts @@ -36,6 +36,7 @@ const useDeployAzorius = () => { multisend: multiSendCallOnly, claimingMasterCopy: erc20ClaimMasterCopy, fractalModuleMasterCopy, + linearVotingMasterCopy: linearERC20VotingMasterCopy, }, addressPrefix, } = useNetworkConfig(); @@ -59,7 +60,6 @@ const useDeployAzorius = () => { return; } const { - linearVotingMasterCopyContract, linearVotingERC721MasterCopyContract, fractalAzoriusMasterCopyContract, fractalModuleMasterCopyContract, @@ -73,7 +73,6 @@ const useDeployAzorius = () => { azoriusContracts = { fractalAzoriusMasterCopyContract: fractalAzoriusMasterCopyContract.asProvider, - linearVotingMasterCopyContract: linearVotingMasterCopyContract.asProvider, linearVotingERC721MasterCopyContract: linearVotingERC721MasterCopyContract.asProvider, azoriusFreezeGuardMasterCopyContract: azoriusFreezeGuardMasterCopyContract.asProvider, }; @@ -103,6 +102,7 @@ const useDeployAzorius = () => { multiSendCallOnly, erc20ClaimMasterCopy, fractalModuleMasterCopy, + linearERC20VotingMasterCopy, undefined, undefined, ); @@ -172,6 +172,7 @@ const useDeployAzorius = () => { multiSendCallOnly, erc20ClaimMasterCopy, fractalModuleMasterCopy, + linearERC20VotingMasterCopy, ], ); diff --git a/src/hooks/safe/useSafeContracts.ts b/src/hooks/safe/useSafeContracts.ts index 0522311cc5..1e7c86d664 100644 --- a/src/hooks/safe/useSafeContracts.ts +++ b/src/hooks/safe/useSafeContracts.ts @@ -4,7 +4,6 @@ import { ERC20FreezeVoting__factory, MultisigFreezeGuard__factory, MultisigFreezeVoting__factory, - LinearERC20Voting__factory, Azorius__factory, LinearERC721Voting__factory, ERC721FreezeVoting__factory, @@ -20,7 +19,6 @@ export default function useSafeContracts() { const { contracts: { - linearVotingMasterCopy, fractalAzoriusMasterCopy, fractalModuleMasterCopy, multisigFreezeGuardMasterCopy, @@ -42,10 +40,6 @@ export default function useSafeContracts() { asProvider: Azorius__factory.connect(fractalAzoriusMasterCopy, provider), }; - const linearVotingMasterCopyContract = { - asSigner: LinearERC20Voting__factory.connect(linearVotingMasterCopy, signerOrProvider), - asProvider: LinearERC20Voting__factory.connect(linearVotingMasterCopy, provider), - }; const linearVotingERC721MasterCopyContract = { asSigner: LinearERC721Voting__factory.connect(linearVotingERC721MasterCopy, signerOrProvider), asProvider: LinearERC721Voting__factory.connect(linearVotingERC721MasterCopy, provider), @@ -89,7 +83,6 @@ export default function useSafeContracts() { return { fractalAzoriusMasterCopyContract, - linearVotingMasterCopyContract, fractalModuleMasterCopyContract, multisigFreezeGuardMasterCopyContract, azoriusFreezeGuardMasterCopyContract, @@ -99,7 +92,6 @@ export default function useSafeContracts() { linearVotingERC721MasterCopyContract, }; }, [ - linearVotingMasterCopy, fractalAzoriusMasterCopy, fractalModuleMasterCopy, multisigFreezeGuardMasterCopy, diff --git a/src/hooks/utils/useCanUserSubmitProposal.ts b/src/hooks/utils/useCanUserSubmitProposal.ts index 20868d584b..00ce3bea1d 100644 --- a/src/hooks/utils/useCanUserSubmitProposal.ts +++ b/src/hooks/utils/useCanUserSubmitProposal.ts @@ -1,6 +1,8 @@ import { Azorius } from '@fractal-framework/fractal-contracts'; import { useState, useCallback, useEffect } from 'react'; -import { getAddress } from 'viem'; +import { getAddress, getContract } from 'viem'; +import { usePublicClient } from 'wagmi'; +import LinearERC20VotingAbi from '../../assets/abi/LinearERC20Voting'; import { SENTINEL_ADDRESS } from '../../constants/common'; import { useFractal } from '../../providers/App/AppProvider'; import { useSafeAPI } from '../../providers/App/hooks/useSafeAPI'; @@ -20,6 +22,8 @@ export function useCanUserCreateProposal() { const baseContracts = useSafeContracts(); const lookupModules = useFractalModules(); const [canUserCreateProposal, setCanUserCreateProposal] = useState(); + const publicClient = usePublicClient(); + /** * 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. @@ -28,7 +32,7 @@ export function useCanUserCreateProposal() { */ const getCanUserCreateProposal = useCallback( async (safeAddress?: string): Promise => { - if (!user.address || !safeAPI) { + if (!user.address || !safeAPI || !publicClient) { return; } @@ -47,9 +51,12 @@ export function useCanUserCreateProposal() { const votingContractAddress = ( await azoriusContract.getStrategies(SENTINEL_ADDRESS, 0) )[1]; - const votingContract = - baseContracts.linearVotingMasterCopyContract.asProvider.attach(votingContractAddress); - const isProposer = await votingContract.isProposer(user.address); + const votingContract = getContract({ + abi: LinearERC20VotingAbi, + address: getAddress(votingContractAddress), + client: publicClient, + }); + const isProposer = await votingContract.read.isProposer([getAddress(user.address)]); return isProposer; } else { return checkIsMultisigOwner(safeInfo.owners); @@ -60,11 +67,12 @@ export function useCanUserCreateProposal() { return checkIsMultisigOwner(owners); } else if (type === GovernanceType.AZORIUS_ERC20) { if (ozLinearVotingContractAddress && user.address && baseContracts) { - const ozLinearVotingContract = - baseContracts.linearVotingMasterCopyContract.asProvider.attach( - ozLinearVotingContractAddress, - ); - return ozLinearVotingContract.isProposer(user.address); + const ozLinearVotingContract = getContract({ + abi: LinearERC20VotingAbi, + address: getAddress(ozLinearVotingContractAddress), + client: publicClient, + }); + return ozLinearVotingContract.read.isProposer([getAddress(user.address)]); } } else if ( type === GovernanceType.AZORIUS_ERC721 && @@ -83,14 +91,15 @@ export function useCanUserCreateProposal() { return; }, [ - safe, - type, - user, - ozLinearVotingContractAddress, + baseContracts, erc721LinearVotingContractAddress, lookupModules, + ozLinearVotingContractAddress, + publicClient, + safe, safeAPI, - baseContracts, + type, + user.address, ], ); useEffect(() => { diff --git a/src/hooks/utils/useMasterCopy.ts b/src/hooks/utils/useMasterCopy.ts index c50f6a9b20..466b803ea5 100644 --- a/src/hooks/utils/useMasterCopy.ts +++ b/src/hooks/utils/useMasterCopy.ts @@ -11,14 +11,13 @@ export function useMasterCopy() { const { getValue, setValue } = useLocalStorage(); const { baseContracts } = useFractal(); const { - contracts: { zodiacModuleProxyFactory }, + contracts: { zodiacModuleProxyFactory, linearVotingMasterCopy }, } = useNetworkConfig(); const publicClient = usePublicClient(); const isOzLinearVoting = useCallback( - (masterCopyAddress: Address) => - masterCopyAddress === baseContracts?.linearVotingMasterCopyContract.asProvider.address, - [baseContracts], + (masterCopyAddress: Address) => masterCopyAddress === linearVotingMasterCopy, + [linearVotingMasterCopy], ); const isOzLinearVotingERC721 = useCallback( (masterCopyAddress: Address) => diff --git a/src/models/AzoriusTxBuilder.ts b/src/models/AzoriusTxBuilder.ts index 2bd6a4569c..b23fbf4b20 100644 --- a/src/models/AzoriusTxBuilder.ts +++ b/src/models/AzoriusTxBuilder.ts @@ -23,6 +23,7 @@ import { } from 'viem'; import ERC20ClaimAbi from '../assets/abi/ERC20Claim'; import GnosisSafeL2Abi from '../assets/abi/GnosisSafeL2'; +import LinearERC20VotingAbi from '../assets/abi/LinearERC20Voting'; import ModuleProxyFactoryAbi from '../assets/abi/ModuleProxyFactory'; import VotesERC20Abi from '../assets/abi/VotesERC20'; import VotesERC20WrapperAbi from '../assets/abi/VotesERC20Wrapper'; @@ -64,6 +65,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { private moduleProxyFactoryAddress: Address; private multiSendCallOnlyAddress: Address; private erc20ClaimMasterCopyAddress: Address; + private linearERC20VotingMasterCopyAddress: Address; private tokenNonce: bigint; private strategyNonce: bigint; @@ -82,6 +84,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { moduleProxyFactoryAddress: Address, multiSendCallOnlyAddress: Address, erc20ClaimMasterCopyAddress: Address, + linearERC20VotingMasterCopyAddress: Address, parentAddress?: Address, parentTokenAddress?: Address, ) { @@ -107,6 +110,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { this.moduleProxyFactoryAddress = moduleProxyFactoryAddress; this.multiSendCallOnlyAddress = multiSendCallOnlyAddress; this.erc20ClaimMasterCopyAddress = erc20ClaimMasterCopyAddress; + this.linearERC20VotingMasterCopyAddress = linearERC20VotingMasterCopyAddress; if (daoData.votingStrategyType === VotingStrategyType.LINEAR_ERC20) { daoData = daoData as AzoriusERC20DAO; @@ -225,7 +229,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { 'deployModule', [ daoData.votingStrategyType === VotingStrategyType.LINEAR_ERC20 - ? this.azoriusContracts!.linearVotingMasterCopyContract.address + ? this.linearERC20VotingMasterCopyAddress : this.azoriusContracts!.linearVotingERC721MasterCopyContract.address, this.encodedStrategySetupData, this.strategyNonce, @@ -434,9 +438,14 @@ export class AzoriusTxBuilder extends BaseTxBuilder { 'Error predicting strategy address - predicted token address was not provided', ); } - const quorumDenominator = ( - await this.azoriusContracts!.linearVotingMasterCopyContract.QUORUM_DENOMINATOR() - ).toBigInt(); + + const linearERC20VotingMasterCopyContract = getContract({ + abi: LinearERC20VotingAbi, + address: this.linearERC20VotingMasterCopyAddress, + client: this.publicClient, + }); + + const quorumDenominator = await linearERC20VotingMasterCopyContract.read.QUORUM_DENOMINATOR(); const encodedStrategyInitParams = encodeAbiParameters( parseAbiParameters('address, address, address, uint32, uint256, uint256, uint256'), [ @@ -450,17 +459,14 @@ export class AzoriusTxBuilder extends BaseTxBuilder { ], ); - const encodedStrategySetupData = - this.azoriusContracts!.linearVotingMasterCopyContract.interface.encodeFunctionData( - 'setUp', - [encodedStrategyInitParams], - ); - if (!isHex(encodedStrategySetupData)) { - throw new Error('Error encoding strategy setup data'); - } + const encodedStrategySetupData = encodeFunctionData({ + abi: LinearERC20VotingAbi, + functionName: 'setUp', + args: [encodedStrategyInitParams], + }); const strategyByteCodeLinear = generateContractByteCodeLinear( - getAddress(this.azoriusContracts!.linearVotingMasterCopyContract.address), + this.linearERC20VotingMasterCopyAddress, ); const strategySalt = keccak256( diff --git a/src/models/TxBuilderFactory.ts b/src/models/TxBuilderFactory.ts index 5133857c73..505455c4fd 100644 --- a/src/models/TxBuilderFactory.ts +++ b/src/models/TxBuilderFactory.ts @@ -39,6 +39,7 @@ export class TxBuilderFactory extends BaseTxBuilder { private multiSendCallOnlyAddress: string; private erc20ClaimMasterCopyAddress: string; private fractalModuleMasterCopyAddress: string; + private linearERC20VotingMasterCopyAddress: string; constructor( signerOrProvider: ethers.Signer | any, @@ -57,6 +58,7 @@ export class TxBuilderFactory extends BaseTxBuilder { multiSendCallOnlyAddress: string, erc20ClaimMasterCopyAddress: string, fractalModuleMasterCopyAddress: string, + linearERC20VotingMasterCopyAddress: string, parentAddress?: string, parentTokenAddress?: string, ) { @@ -82,6 +84,7 @@ export class TxBuilderFactory extends BaseTxBuilder { this.multiSendCallOnlyAddress = multiSendCallOnlyAddress; this.erc20ClaimMasterCopyAddress = erc20ClaimMasterCopyAddress; this.fractalModuleMasterCopyAddress = fractalModuleMasterCopyAddress; + this.linearERC20VotingMasterCopyAddress = linearERC20VotingMasterCopyAddress; } public setSafeContract(safeAddress: Address) { @@ -187,6 +190,7 @@ export class TxBuilderFactory extends BaseTxBuilder { getAddress(this.moduleProxyFactoryAddress), getAddress(this.multiSendCallOnlyAddress), getAddress(this.erc20ClaimMasterCopyAddress), + getAddress(this.linearERC20VotingMasterCopyAddress), this.parentAddress ? getAddress(this.parentAddress) : undefined, this.parentTokenAddress ? getAddress(this.parentTokenAddress) : undefined, ); diff --git a/src/types/fractal.ts b/src/types/fractal.ts index 8235c72523..f60c21326c 100644 --- a/src/types/fractal.ts +++ b/src/types/fractal.ts @@ -1,6 +1,5 @@ import { FractalModule, - LinearERC20Voting, Azorius, AzoriusFreezeGuard, ERC20FreezeVoting, @@ -319,7 +318,6 @@ export interface NodeHierarchy { export interface FractalContracts { fractalAzoriusMasterCopyContract: ContractConnection; - linearVotingMasterCopyContract: ContractConnection; linearVotingERC721MasterCopyContract: ContractConnection; fractalModuleMasterCopyContract: ContractConnection; multisigFreezeGuardMasterCopyContract: ContractConnection; diff --git a/src/types/strategyAzorius.ts b/src/types/strategyAzorius.ts index d140342323..e46b3d25f5 100644 --- a/src/types/strategyAzorius.ts +++ b/src/types/strategyAzorius.ts @@ -1,13 +1,11 @@ import { Azorius, - LinearERC20Voting, AzoriusFreezeGuard, LinearERC721Voting, } from '@fractal-framework/fractal-contracts'; export interface AzoriusContracts { fractalAzoriusMasterCopyContract: Azorius; - linearVotingMasterCopyContract: LinearERC20Voting; linearVotingERC721MasterCopyContract: LinearERC721Voting; azoriusFreezeGuardMasterCopyContract: AzoriusFreezeGuard; } diff --git a/src/utils/azorius.ts b/src/utils/azorius.ts index 6b3db3383a..1910921bf5 100644 --- a/src/utils/azorius.ts +++ b/src/utils/azorius.ts @@ -1,12 +1,9 @@ -import { - Azorius, - LinearERC20Voting, - LinearERC721Voting, -} from '@fractal-framework/fractal-contracts'; +import { Azorius, LinearERC721Voting } from '@fractal-framework/fractal-contracts'; import { ProposalExecutedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/Azorius'; -import { VotedEvent as ERC20VotedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/LinearERC20Voting'; import { VotedEvent as ERC721VotedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/LinearERC721Voting'; import { SafeMultisigTransactionWithTransfersResponse } from '@safe-global/safe-service-client'; +import { GetContractEventsReturnType, GetContractReturnType, PublicClient } from 'viem'; +import LinearERC20VotingAbi from '../assets/abi/LinearERC20Voting'; import { strategyFractalProposalStates } from '../constants/strategy'; import { logError } from '../helpers/errorLogging'; @@ -40,7 +37,9 @@ export const getAzoriusProposalState = async ( }; const getQuorum = async ( - erc20StrategyContract: LinearERC20Voting | undefined, + erc20StrategyContract: + | GetContractReturnType + | undefined, erc721StrategyContract: LinearERC721Voting | undefined, strategyType: VotingStrategyType, proposalId: bigint, @@ -48,7 +47,7 @@ const getQuorum = async ( let quorum; if (strategyType === VotingStrategyType.LINEAR_ERC20 && erc20StrategyContract) { - quorum = (await erc20StrategyContract.quorumVotes(proposalId)).toBigInt(); + quorum = await erc20StrategyContract.read.quorumVotes([Number(proposalId)]); } else if (strategyType === VotingStrategyType.LINEAR_ERC721 && erc721StrategyContract) { quorum = (await erc721StrategyContract.quorumThreshold()).toBigInt(); } else { @@ -59,7 +58,7 @@ const getQuorum = async ( }; export const getProposalVotesSummary = async ( - erc20Strategy: LinearERC20Voting | undefined, + erc20Strategy: GetContractReturnType | undefined, erc721Strategy: LinearERC721Voting | undefined, strategyType: VotingStrategyType, proposalId: bigint, @@ -71,12 +70,14 @@ export const getProposalVotesSummary = async ( } if (erc20Strategy !== undefined) { - const { yesVotes, noVotes, abstainVotes } = await erc20Strategy.getProposalVotes(proposalId); + const [yesVotes, noVotes, abstainVotes] = await erc20Strategy.read.getProposalVotes([ + Number(proposalId), + ]); return { - yes: yesVotes.toBigInt(), - no: noVotes.toBigInt(), - abstain: abstainVotes.toBigInt(), + yes: yesVotes, + no: noVotes, + abstain: abstainVotes, quorum: await getQuorum(erc20Strategy, erc721Strategy, strategyType, proposalId), }; } else if (erc721Strategy !== undefined) { @@ -111,7 +112,7 @@ export const getProposalVotesSummary = async ( }; const getProposalVotes = ( - erc20VotedEvents: ERC20VotedEvent[] | undefined, + erc20VotedEvents: GetContractEventsReturnType | undefined, erc721VotedEvents: ERC721VotedEvent[] | undefined, proposalId: bigint, ): (ProposalVote | ERC721ProposalVote)[] => { @@ -121,16 +122,23 @@ const getProposalVotes = ( } if (erc20VotedEvents !== undefined) { - const erc20ProposalVoteEvents = erc20VotedEvents.filter( - voteEvent => proposalId === BigInt(voteEvent.args.proposalId), - ); + const erc20ProposalVoteEvents = erc20VotedEvents.filter(voteEvent => { + if (!voteEvent.args.proposalId) return false; + return proposalId === BigInt(voteEvent.args.proposalId); + }); - return erc20ProposalVoteEvents.map(({ args: { voter, voteType, weight, ...rest } }) => ({ - ...rest, - weight: weight.toBigInt(), - voter, - choice: VOTE_CHOICES[voteType], - })); + const events = []; + for (const event of erc20ProposalVoteEvents) { + if (!event.args.voteType || !event.args.weight || !event.args.voter) { + continue; + } + events.push({ + weight: event.args.weight, + voter: event.args.voter, + choice: VOTE_CHOICES[event.args.voteType], + }); + } + return events; } else if (erc721VotedEvents !== undefined) { const erc721ProposalVoteEvents = erc721VotedEvents.filter( voteEvent => proposalId === BigInt(voteEvent.args.proposalId), @@ -149,14 +157,18 @@ const getProposalVotes = ( }; export const mapProposalCreatedEventToProposal = async ( - erc20StrategyContract: LinearERC20Voting | undefined, + erc20StrategyContract: + | GetContractReturnType + | undefined, erc721StrategyContract: LinearERC721Voting | undefined, strategyType: VotingStrategyType, proposalId: bigint, proposer: string, azoriusContract: Azorius, provider: Providers, - erc20VotedEvents: Promise, + erc20VotedEvents: Promise< + GetContractEventsReturnType | undefined + >, erc721VotedEvents: Promise, executedEvents: Promise, data?: ProposalData, @@ -174,20 +186,26 @@ export const mapProposalCreatedEventToProposal = async ( abstainVotes: 0n, }; - let stratProposalVotes; if (erc20StrategyContract !== undefined) { - stratProposalVotes = await erc20StrategyContract.getProposalVotes(proposalId); + const stratProposalVotes = await erc20StrategyContract.read.getProposalVotes([ + Number(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) { - stratProposalVotes = await erc721StrategyContract.getProposalVotes(proposalId); + const stratProposalVotes = await erc721StrategyContract.getProposalVotes(proposalId); + proposalVotes.startBlock = stratProposalVotes.startBlock; + proposalVotes.endBlock = stratProposalVotes.endBlock; + proposalVotes.noVotes = stratProposalVotes.noVotes.toBigInt(); + proposalVotes.yesVotes = stratProposalVotes.yesVotes.toBigInt(); + proposalVotes.abstainVotes = stratProposalVotes.abstainVotes.toBigInt(); } else { logError('we need a strat!'); throw new Error('we need a strategy!'); } - proposalVotes.startBlock = stratProposalVotes.startBlock; - proposalVotes.endBlock = stratProposalVotes.endBlock; - proposalVotes.noVotes = stratProposalVotes.noVotes.toBigInt(); - proposalVotes.yesVotes = stratProposalVotes.yesVotes.toBigInt(); - proposalVotes.abstainVotes = stratProposalVotes.abstainVotes.toBigInt(); const quorum = await getQuorum( erc20StrategyContract,