diff --git a/src/assets/abi/LinearERC721Voting.ts b/src/assets/abi/LinearERC721Voting.ts new file mode 100644 index 0000000000..5a5a5f197e --- /dev/null +++ b/src/assets/abi/LinearERC721Voting.ts @@ -0,0 +1,799 @@ +const LinearERC721VotingAbi = [ + { + inputs: [ + { + internalType: 'uint256', + name: 'tokenId', + type: 'uint256', + }, + ], + name: 'IdAlreadyVoted', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'tokenId', + type: 'uint256', + }, + ], + name: 'IdNotOwned', + type: 'error', + }, + { + inputs: [], + name: 'InvalidBasisNumerator', + type: 'error', + }, + { + inputs: [], + name: 'InvalidParams', + type: 'error', + }, + { + inputs: [], + name: 'InvalidProposal', + type: 'error', + }, + { + inputs: [], + name: 'InvalidTokenAddress', + type: 'error', + }, + { + inputs: [], + name: 'InvalidVote', + type: 'error', + }, + { + inputs: [], + name: 'NoVotingWeight', + type: 'error', + }, + { + inputs: [], + name: 'OnlyAzorius', + type: 'error', + }, + { + inputs: [], + name: 'TokenAlreadySet', + type: 'error', + }, + { + inputs: [], + name: 'TokenNotSet', + type: 'error', + }, + { + inputs: [], + name: 'VotingEnded', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'azoriusModule', + type: 'address', + }, + ], + name: 'AzoriusSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'basisNumerator', + type: 'uint256', + }, + ], + name: 'BasisNumeratorUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'token', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'weight', + type: 'uint256', + }, + ], + name: 'GovernanceTokenAdded', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'token', + type: 'address', + }, + ], + name: 'GovernanceTokenRemoved', + 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: 'uint32', + name: 'proposalId', + type: 'uint32', + }, + { + indexed: false, + internalType: 'uint32', + name: 'votingEndBlock', + type: 'uint32', + }, + ], + name: 'ProposalInitialized', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'proposerThreshold', + type: 'uint256', + }, + ], + name: 'ProposerThresholdUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'quorumThreshold', + type: 'uint256', + }, + ], + name: 'QuorumThresholdUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'azoriusModule', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + ], + name: 'StrategySetUp', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'voter', + type: 'address', + }, + { + indexed: false, + internalType: 'uint32', + name: 'proposalId', + type: 'uint32', + }, + { + indexed: false, + internalType: 'uint8', + name: 'voteType', + type: 'uint8', + }, + { + indexed: false, + internalType: 'address[]', + name: 'tokenAddresses', + type: 'address[]', + }, + { + indexed: false, + internalType: 'uint256[]', + name: 'tokenIds', + type: 'uint256[]', + }, + ], + name: 'Voted', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint32', + name: 'votingPeriod', + type: 'uint32', + }, + ], + name: 'VotingPeriodUpdated', + type: 'event', + }, + { + inputs: [], + name: 'BASIS_DENOMINATOR', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_tokenAddress', + type: 'address', + }, + { + internalType: 'uint256', + name: '_weight', + type: 'uint256', + }, + ], + name: 'addGovernanceToken', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'azoriusModule', + outputs: [ + { + internalType: 'contract IAzorius', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'basisNumerator', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getAllTokenAddresses', + outputs: [ + { + internalType: 'address[]', + name: '', + type: 'address[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint32', + name: '_proposalId', + type: 'uint32', + }, + ], + name: 'getProposalVotes', + outputs: [ + { + internalType: 'uint256', + name: 'noVotes', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'yesVotes', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'abstainVotes', + type: 'uint256', + }, + { + internalType: 'uint32', + name: 'startBlock', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'endBlock', + type: 'uint32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_tokenAddress', + type: 'address', + }, + ], + name: 'getTokenWeight', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint32', + name: '_proposalId', + type: 'uint32', + }, + { + internalType: 'address', + name: '_tokenAddress', + type: 'address', + }, + { + internalType: 'uint256', + name: '_tokenId', + type: 'uint256', + }, + ], + name: 'hasVoted', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes', + name: '_data', + type: 'bytes', + }, + ], + name: 'initializeProposal', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint32', + name: '_proposalId', + type: 'uint32', + }, + ], + name: 'isPassed', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_address', + type: 'address', + }, + ], + name: 'isProposer', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_yesVotes', + type: 'uint256', + }, + { + internalType: 'uint256', + name: '_noVotes', + type: 'uint256', + }, + ], + name: 'meetsBasis', + 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: 'uint256', + name: '', + type: 'uint256', + }, + ], + name: 'proposalVotes', + outputs: [ + { + internalType: 'uint32', + name: 'votingStartBlock', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'votingEndBlock', + type: 'uint32', + }, + { + internalType: 'uint256', + name: 'noVotes', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'yesVotes', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'abstainVotes', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'proposerThreshold', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'quorumThreshold', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_tokenAddress', + type: 'address', + }, + ], + name: 'removeGovernanceToken', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'renounceOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_azoriusModule', + type: 'address', + }, + ], + name: 'setAzorius', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes', + name: 'initializeParams', + type: 'bytes', + }, + ], + name: 'setUp', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + name: 'tokenAddresses', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + name: 'tokenWeights', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'transferOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_basisNumerator', + type: 'uint256', + }, + ], + name: 'updateBasisNumerator', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_proposerThreshold', + type: 'uint256', + }, + ], + name: 'updateProposerThreshold', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_quorumThreshold', + type: 'uint256', + }, + ], + name: 'updateQuorumThreshold', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint32', + name: '_votingPeriod', + type: 'uint32', + }, + ], + name: 'updateVotingPeriod', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint32', + name: '_proposalId', + type: 'uint32', + }, + { + internalType: 'uint8', + name: '_voteType', + type: 'uint8', + }, + { + internalType: 'address[]', + name: '_tokenAddresses', + type: 'address[]', + }, + { + internalType: 'uint256[]', + name: '_tokenIds', + type: 'uint256[]', + }, + ], + name: 'vote', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint32', + name: '_proposalId', + type: 'uint32', + }, + ], + name: 'votingEndBlock', + outputs: [ + { + internalType: 'uint32', + name: '', + type: 'uint32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'votingPeriod', + outputs: [ + { + internalType: 'uint32', + name: '', + type: 'uint32', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const; + +export default LinearERC721VotingAbi; diff --git a/src/components/Proposals/ProposalVotes/context/VoteContext.tsx b/src/components/Proposals/ProposalVotes/context/VoteContext.tsx index fe2f35f5b1..30f3ec4e0e 100644 --- a/src/components/Proposals/ProposalVotes/context/VoteContext.tsx +++ b/src/components/Proposals/ProposalVotes/context/VoteContext.tsx @@ -62,8 +62,8 @@ export function VoteContextProvider({ const { loadVotingWeight } = useSnapshotProposal(proposal as SnapshotProposal); const { remainingTokenIds, getUserERC721VotingTokens } = useUserERC721VotingTokens( + null, proposal.proposalId, - undefined, true, ); const { isSnapshotProposal } = useSnapshotProposal(proposal); diff --git a/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts b/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts index 6a07e38970..95afa5b4ea 100644 --- a/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts +++ b/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts @@ -1,15 +1,14 @@ -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 ERC721VotedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/LinearERC721Voting'; import { Dispatch, useEffect, useMemo } from 'react'; import { getAddress, getContract, GetContractReturnType, Hex, PublicClient } from 'viem'; import { usePublicClient } from 'wagmi'; 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'; @@ -34,7 +33,9 @@ const proposalCreatedEventListener = ( erc20StrategyContract: | GetContractReturnType | undefined, - erc721StrategyContract: LinearERC721Voting | undefined, + erc721StrategyContract: + | GetContractReturnType + | undefined, provider: Providers, strategyType: VotingStrategyType, decode: (value: string, to: string, data?: string | undefined) => Promise, @@ -91,33 +92,6 @@ const proposalCreatedEventListener = ( }; }; -const erc721VotedEventListener = ( - erc721StrategyContract: LinearERC721Voting, - strategyType: VotingStrategyType, - dispatch: Dispatch, -): TypedListener => { - return async (voter, proposalId, voteType, tokenAddresses, tokenIds) => { - const votesSummary = await getProposalVotesSummary( - undefined, - erc721StrategyContract, - strategyType, - BigInt(proposalId), - ); - - dispatch({ - type: FractalGovernanceAction.UPDATE_NEW_AZORIUS_ERC721_VOTE, - payload: { - proposalId: proposalId.toString(), - voter, - support: voteType, - tokenAddresses, - tokenIds: tokenIds.map(tokenId => tokenId.toString()), - votesSummary, - }, - }); - }; -}; - export const useAzoriusListeners = () => { const { action, @@ -165,14 +139,16 @@ export const useAzoriusListeners = () => { }, [ozLinearVotingContractAddress, publicClient]); const erc721StrategyContract = useMemo(() => { - if (!baseContracts || !erc721LinearVotingContractAddress) { + if (!erc721LinearVotingContractAddress || !publicClient) { return undefined; } - return baseContracts.linearVotingERC721MasterCopyContract.asProvider.attach( - erc721LinearVotingContractAddress, - ); - }, [baseContracts, erc721LinearVotingContractAddress]); + return getContract({ + abi: LinearERC721VotingAbi, + address: getAddress(erc721LinearVotingContractAddress), + client: publicClient, + }); + }, [erc721LinearVotingContractAddress, publicClient]); useEffect(() => { if (!azoriusContract || !provider || !strategyType) { @@ -248,19 +224,45 @@ export const useAzoriusListeners = () => { return; } - const votedEvent = erc721StrategyContract.filters.Voted(); - const listener = erc721VotedEventListener( - erc721StrategyContract, - strategyType, - action.dispatch, - ); + const unwatch = erc721StrategyContract.watchEvent.Voted({ + onLogs: async logs => { + for (const log of logs) { + if ( + !log.args.proposalId || + !log.args.voter || + !log.args.voteType || + !log.args.tokenAddresses || + !log.args.tokenIds + ) { + continue; + } - erc721StrategyContract.on(votedEvent, listener); + const votesSummary = await getProposalVotesSummary( + undefined, + erc721StrategyContract, + strategyType, + BigInt(log.args.proposalId), + ); + + action.dispatch({ + type: FractalGovernanceAction.UPDATE_NEW_AZORIUS_ERC721_VOTE, + payload: { + proposalId: log.args.proposalId.toString(), + voter: log.args.voter, + support: log.args.voteType, + tokenAddresses: log.args.tokenAddresses.map(tokenAddress => tokenAddress), + tokenIds: log.args.tokenIds.map(tokenId => tokenId.toString()), + votesSummary, + }, + }); + } + }, + }); return () => { - erc721StrategyContract.off(votedEvent, listener); + unwatch(); }; - }, [action.dispatch, erc721StrategyContract, strategyType]); + }, [action, erc721StrategyContract, strategyType]); useEffect(() => { if (!azoriusContract) { diff --git a/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts b/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts index ad2c8c2289..73cec33533 100644 --- a/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts +++ b/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts @@ -1,9 +1,7 @@ -import { LinearERC721Voting } from '@fractal-framework/fractal-contracts'; import { Azorius, ProposalExecutedEvent, } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/Azorius'; -import { VotedEvent as ERC721VotedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/LinearERC721Voting'; import { useCallback, useEffect, useMemo, useRef } from 'react'; import { GetContractEventsReturnType, @@ -15,6 +13,7 @@ import { } from 'viem'; import { usePublicClient } from 'wagmi'; 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'; @@ -73,14 +72,16 @@ export const useAzoriusProposals = () => { }, [ozLinearVotingContractAddress, publicClient]); const erc721StrategyContract = useMemo(() => { - if (!baseContracts || !erc721LinearVotingContractAddress) { + if (!erc721LinearVotingContractAddress || !publicClient) { return undefined; } - return baseContracts.linearVotingERC721MasterCopyContract.asProvider.attach( - erc721LinearVotingContractAddress, - ); - }, [baseContracts, erc721LinearVotingContractAddress]); + return getContract({ + abi: LinearERC721VotingAbi, + address: getAddress(erc721LinearVotingContractAddress), + client: publicClient, + }); + }, [erc721LinearVotingContractAddress, publicClient]); const erc20VotedEvents = useMemo(async () => { if (!erc20StrategyContract) { @@ -95,10 +96,7 @@ export const useAzoriusProposals = () => { return; } - const filter = erc721StrategyContract.filters.Voted(); - const events = await erc721StrategyContract.queryFilter(filter); - - return events; + return erc721StrategyContract?.getEvents.Voted(); }, [erc721StrategyContract]); const executedEvents = useMemo(async () => { @@ -128,12 +126,16 @@ export const useAzoriusProposals = () => { _erc20StrategyContract: | GetContractReturnType | undefined, - _erc721StrategyContract: LinearERC721Voting | undefined, + _erc721StrategyContract: + | GetContractReturnType + | undefined, _strategyType: VotingStrategyType | undefined, _erc20VotedEvents: Promise< GetContractEventsReturnType | undefined >, - _erc721VotedEvents: Promise, + _erc721VotedEvents: Promise< + GetContractEventsReturnType | undefined + >, _executedEvents: Promise, _provider: Providers | undefined, _decode: ( diff --git a/src/hooks/DAO/loaders/governance/useERC721LinearStrategy.ts b/src/hooks/DAO/loaders/governance/useERC721LinearStrategy.ts index 54fca51bfe..ce478e6b31 100644 --- a/src/hooks/DAO/loaders/governance/useERC721LinearStrategy.ts +++ b/src/hooks/DAO/loaders/governance/useERC721LinearStrategy.ts @@ -1,9 +1,7 @@ -import { TypedListener } from '@fractal-framework/fractal-contracts/dist/typechain-types/common'; -import { - VotingPeriodUpdatedEvent, - QuorumThresholdUpdatedEvent, -} from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/LinearERC721Voting'; -import { useCallback, useEffect } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; +import { getAddress, getContract } from 'viem'; +import { usePublicClient } from 'wagmi'; +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'; @@ -20,25 +18,31 @@ export const useERC721LinearStrategy = () => { const provider = useEthersProvider(); const { getTimeDuration } = useTimeHelpers(); const baseContracts = useSafeContracts(); + const publicClient = usePublicClient(); + + const erc721LinearVotingContract = useMemo(() => { + if (!erc721LinearVotingContractAddress || !publicClient) { + return; + } + + return getContract({ + abi: LinearERC721VotingAbi, + address: getAddress(erc721LinearVotingContractAddress), + client: publicClient, + }); + }, [erc721LinearVotingContractAddress, publicClient]); + const loadERC721Strategy = useCallback(async () => { - if ( - !erc721LinearVotingContractAddress || - !azoriusContractAddress || - !provider || - !baseContracts - ) { + if (!azoriusContractAddress || !provider || !baseContracts || !erc721LinearVotingContract) { return {}; } - const erc721LinearVotingContract = - baseContracts.linearVotingERC721MasterCopyContract.asProvider.attach( - erc721LinearVotingContractAddress, - ); + const azoriusContract = baseContracts.fractalAzoriusMasterCopyContract.asProvider.attach(azoriusContractAddress); const [votingPeriodBlocks, quorumThreshold, timeLockPeriod] = await Promise.all([ - erc721LinearVotingContract.votingPeriod(), - erc721LinearVotingContract.quorumThreshold(), + erc721LinearVotingContract.read.votingPeriod(), + erc721LinearVotingContract.read.quorumThreshold(), azoriusContract.timelockPeriod(), ]); @@ -50,7 +54,7 @@ export const useERC721LinearStrategy = () => { formatted: getTimeDuration(votingPeriodValue), }, quorumThreshold: { - value: quorumThreshold.toBigInt(), + value: quorumThreshold, formatted: quorumThreshold.toString(), }, timeLockPeriod: { @@ -61,58 +65,63 @@ export const useERC721LinearStrategy = () => { }; action.dispatch({ type: FractalGovernanceAction.SET_STRATEGY, payload: votingData }); }, [ - erc721LinearVotingContractAddress, + action, azoriusContractAddress, + baseContracts, + erc721LinearVotingContract, getTimeDuration, - action, provider, - baseContracts, ]); useEffect(() => { - if (!erc721LinearVotingContractAddress || !baseContracts) { + if (!erc721LinearVotingContract) { return; } - const erc721LinearVotingContract = - baseContracts.linearVotingERC721MasterCopyContract.asProvider.attach( - erc721LinearVotingContractAddress, - ); - const votingPeriodfilter = erc721LinearVotingContract.filters.VotingPeriodUpdated(); - const listener: TypedListener = votingPeriod => { - action.dispatch({ - type: FractalGovernanceAction.UPDATE_VOTING_PERIOD, - payload: BigInt(votingPeriod), - }); - }; - erc721LinearVotingContract.on(votingPeriodfilter, listener); + + const unwatch = erc721LinearVotingContract.watchEvent.VotingPeriodUpdated({ + onLogs: logs => { + for (const log of logs) { + if (!log.args.votingPeriod) { + continue; + } + + action.dispatch({ + type: FractalGovernanceAction.UPDATE_VOTING_PERIOD, + payload: BigInt(log.args.votingPeriod), + }); + } + }, + }); + return () => { - erc721LinearVotingContract.off(votingPeriodfilter, listener); + unwatch(); }; - }, [erc721LinearVotingContractAddress, action, baseContracts]); + }, [action, erc721LinearVotingContract]); useEffect(() => { - if (!erc721LinearVotingContractAddress || !baseContracts) { + if (!erc721LinearVotingContract) { return; } - const erc721LinearVotingContract = - baseContracts.linearVotingERC721MasterCopyContract.asProvider.attach( - erc721LinearVotingContractAddress, - ); - const quorumThresholdUpdatedFilter = - erc721LinearVotingContract.filters.QuorumThresholdUpdated(); - const quorumThresholdUpdatedListener: TypedListener< - QuorumThresholdUpdatedEvent - > = quorumThreshold => { - action.dispatch({ - type: FractalGovernanceAction.UPDATE_VOTING_QUORUM_THRESHOLD, - payload: quorumThreshold.toBigInt(), - }); - }; - erc721LinearVotingContract.on(quorumThresholdUpdatedFilter, quorumThresholdUpdatedListener); + + const unwatch = erc721LinearVotingContract.watchEvent.QuorumThresholdUpdated({ + onLogs: logs => { + for (const log of logs) { + if (!log.args.quorumThreshold) { + continue; + } + + action.dispatch({ + type: FractalGovernanceAction.UPDATE_VOTING_QUORUM_THRESHOLD, + payload: log.args.quorumThreshold, + }); + } + }, + }); + return () => { - erc721LinearVotingContract.off(quorumThresholdUpdatedFilter, quorumThresholdUpdatedListener); + unwatch(); }; - }, [erc721LinearVotingContractAddress, action, baseContracts]); + }, [erc721LinearVotingContract, action]); return loadERC721Strategy; }; diff --git a/src/hooks/DAO/loaders/governance/useERC721Tokens.ts b/src/hooks/DAO/loaders/governance/useERC721Tokens.ts index d2060b2741..6451f04c78 100644 --- a/src/hooks/DAO/loaders/governance/useERC721Tokens.ts +++ b/src/hooks/DAO/loaders/governance/useERC721Tokens.ts @@ -1,6 +1,7 @@ import { useCallback } from 'react'; import { erc721Abi, getAddress, getContract, zeroAddress } from 'viem'; import { usePublicClient } from 'wagmi'; +import LinearERC721VotingAbi from '../../../../assets/abi/LinearERC721Voting'; import { useFractal } from '../../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; import { ERC721TokenData } from '../../../../types'; @@ -17,19 +18,20 @@ export default function useERC721Tokens() { if (!erc721LinearVotingContractAddress || !baseContracts || !publicClient) { return; } - const erc721LinearVotingContract = - baseContracts.linearVotingERC721MasterCopyContract.asProvider.attach( - erc721LinearVotingContractAddress, - ); - const addresses = await erc721LinearVotingContract.getAllTokenAddresses(); + const erc721LinearVotingContract = getContract({ + abi: LinearERC721VotingAbi, + address: getAddress(erc721LinearVotingContractAddress), + client: publicClient, + }); + const addresses = await erc721LinearVotingContract.read.getAllTokenAddresses(); const erc721Tokens: ERC721TokenData[] = await Promise.all( addresses.map(async address => { const tokenContract = getContract({ abi: erc721Abi, - address: getAddress(address), + address: address, client: publicClient, }); - const votingWeight = (await erc721LinearVotingContract.getTokenWeight(address)).toBigInt(); + const votingWeight = await erc721LinearVotingContract.read.getTokenWeight([address]); const [name, symbol, tokenMintEvents, tokenBurnEvents] = await Promise.all([ tokenContract.read.name(), tokenContract.read.symbol(), @@ -37,7 +39,7 @@ export default function useERC721Tokens() { tokenContract.getEvents.Transfer({ to: zeroAddress }), ]); const totalSupply = BigInt(tokenMintEvents.length - tokenBurnEvents.length); - return { name, symbol, address: getAddress(address), votingWeight, totalSupply }; + return { name, symbol, address: address, votingWeight, totalSupply }; }), ); diff --git a/src/hooks/DAO/proposal/useCastVote.ts b/src/hooks/DAO/proposal/useCastVote.ts index 5c38014a23..2ceb74831d 100644 --- a/src/hooks/DAO/proposal/useCastVote.ts +++ b/src/hooks/DAO/proposal/useCastVote.ts @@ -6,6 +6,7 @@ import { toast } from 'react-toastify'; import { getAddress, getContract } from 'viem'; import { useWalletClient } from 'wagmi'; import LinearERC20VotingAbi from '../../../assets/abi/LinearERC20Voting'; +import LinearERC721VotingAbi from '../../../assets/abi/LinearERC721Voting'; import { useVoteContext } from '../../../components/Proposals/ProposalVotes/context/VoteContext'; import { logError } from '../../../helpers/errorLogging'; import { useFractal } from '../../../providers/App/AppProvider'; @@ -17,7 +18,6 @@ import { ExtendedSnapshotProposal, } from '../../../types'; import encryptWithShutter from '../../../utils/shutter'; -import useSafeContracts from '../../safe/useSafeContracts'; import { useTransaction } from '../../utils/useTransaction'; import useSnapshotSpaceName from '../loaders/snapshot/useSnapshotSpaceName'; import useUserERC721VotingTokens from './useUserERC721VotingTokens'; @@ -42,7 +42,6 @@ const useCastVote = ({ user: { address }, }, } = useFractal(); - const baseContracts = useSafeContracts(); const daoSnapshotSpaceName = useSnapshotSpaceName(); const signer = useEthersSigner(); const client = useMemo(() => { @@ -55,9 +54,10 @@ const useCastVote = ({ const azoriusGovernance = useMemo(() => governance as AzoriusGovernance, [governance]); const { type } = azoriusGovernance; - const [contractCallCastVote, contractCallPending, contractCallCastVoteViem] = useTransaction(); + const [, contractCallPending, contractCallCastVoteViem] = useTransaction(); const { remainingTokenIds, remainingTokenAddresses } = useUserERC721VotingTokens( + null, proposal.proposalId, ); const { getCanVote, getHasVoted } = useVoteContext(); @@ -110,20 +110,21 @@ const useCastVote = ({ } else if ( type === GovernanceType.AZORIUS_ERC721 && erc721LinearVotingContractAddress && - baseContracts + walletClient ) { - const erc721LinearVotingContract = - baseContracts.linearVotingERC721MasterCopyContract.asSigner.attach( - erc721LinearVotingContractAddress, - ); - contractCallCastVote({ + const erc721LinearVotingContract = getContract({ + abi: LinearERC721VotingAbi, + address: getAddress(erc721LinearVotingContractAddress), + client: walletClient, + }); + contractCallCastVoteViem({ contractFn: () => - erc721LinearVotingContract.vote( - proposal.proposalId, + erc721LinearVotingContract.write.vote([ + Number(proposal.proposalId), vote, - remainingTokenAddresses, - remainingTokenIds, - ), + remainingTokenAddresses.map(a => getAddress(a)), + remainingTokenIds.map(i => BigInt(i)), + ]), pendingMessage: t('pendingCastVote'), failedMessage: t('failedCastVote'), successMessage: t('successCastVote'), @@ -137,8 +138,6 @@ const useCastVote = ({ } }, [ - baseContracts, - contractCallCastVote, contractCallCastVoteViem, erc721LinearVotingContractAddress, getCanVote, diff --git a/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts b/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts index 2c9a0fbf9e..90c93e03e7 100644 --- a/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts +++ b/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts @@ -1,11 +1,8 @@ -import { - Azorius, - LinearERC721Voting__factory, - LinearERC721Voting, -} from '@fractal-framework/fractal-contracts'; +import { Azorius } from '@fractal-framework/fractal-contracts'; import { useState, useEffect, useCallback } from 'react'; -import { erc721Abi, getAddress, getContract } from 'viem'; +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'; @@ -51,7 +48,7 @@ export default function useUserERC721VotingTokens( const { erc721Tokens } = azoriusGovernance; const getUserERC721VotingTokens = useCallback( - async (_safeAddress: string | null, _proposalId?: string) => { + async (_safeAddress: string | null, _proposalId?: number) => { const totalTokenAddresses: string[] = []; const totalTokenIds: string[] = []; const tokenAddresses: string[] = []; @@ -59,7 +56,9 @@ export default function useUserERC721VotingTokens( const userERC721Tokens = new Map>(); let govTokens = erc721Tokens; - let votingContract: LinearERC721Voting | undefined; + let votingContract: + | GetContractReturnType + | undefined; if (!baseContracts || !signerOrProvider || !daoAddress || !publicClient || !safeAPI) { return { @@ -81,13 +80,18 @@ export default function useUserERC721VotingTokens( const votingContractAddress = ( await azoriusContract.getStrategies(SENTINEL_ADDRESS, 0) )[1]; - votingContract = LinearERC721Voting__factory.connect( - votingContractAddress, - signerOrProvider, - ); - const addresses = await votingContract.getAllTokenAddresses(); + votingContract = getContract({ + abi: LinearERC721VotingAbi, + address: getAddress(votingContractAddress), + client: publicClient, + }); + const addresses = await votingContract.read.getAllTokenAddresses(); govTokens = await Promise.all( addresses.map(async tokenAddress => { + if (!votingContract) { + throw new Error('voting contract is undefined'); + } + const tokenContract = getContract({ abi: erc721Abi, address: getAddress(tokenAddress), @@ -95,7 +99,7 @@ export default function useUserERC721VotingTokens( }); const [votingWeight, name, symbol] = await Promise.all([ - (await votingContract!.getTokenWeight(tokenAddress)).toBigInt(), + votingContract.read.getTokenWeight([tokenAddress]), tokenContract.read.name(), tokenContract.read.symbol(), ]); @@ -107,9 +111,11 @@ export default function useUserERC721VotingTokens( } if (erc721LinearVotingContractAddress && !votingContract) { - votingContract = baseContracts.linearVotingERC721MasterCopyContract.asProvider.attach( - erc721LinearVotingContractAddress, - ); + votingContract = getContract({ + abi: LinearERC721VotingAbi, + address: getAddress(erc721LinearVotingContractAddress), + client: publicClient, + }); } if (!govTokens || !votingContract || !user.address) { @@ -173,14 +179,18 @@ export default function useUserERC721VotingTokens( // Maybe, if we will encounter need to wider support of ERC-1155 - we will bring it and improve this piece of crap as well :D await Promise.all( [...tokenIdsSet.values()].map(async tokenId => { + if (!votingContract) { + throw new Error('voting contract is undefined'); + } + totalTokenAddresses.push(tokenAddress); totalTokenIds.push(tokenId); if (_proposalId) { - const tokenVoted = await votingContract!.hasVoted( + const tokenVoted = await votingContract.read.hasVoted([ _proposalId, - tokenAddress, - tokenId, - ); + getAddress(tokenAddress), + BigInt(tokenId), + ]); if (!tokenVoted) { tokenAddresses.push(tokenAddress); tokenIds.push(tokenId); @@ -212,7 +222,7 @@ export default function useUserERC721VotingTokens( ); const loadUserERC721VotingTokens = useCallback(async () => { - const tokensInfo = await getUserERC721VotingTokens(safeAddress, proposalId); + const tokensInfo = await getUserERC721VotingTokens(safeAddress, Number(proposalId)); if (tokensInfo) { setTotalVotingTokenAddresses(tokensInfo.totalVotingTokenAddresses); setTotalVotingTokenIds(tokensInfo.totalVotingTokenIds); diff --git a/src/hooks/DAO/useBuildDAOTx.ts b/src/hooks/DAO/useBuildDAOTx.ts index d40ab0721e..ee4aed99db 100644 --- a/src/hooks/DAO/useBuildDAOTx.ts +++ b/src/hooks/DAO/useBuildDAOTx.ts @@ -18,7 +18,6 @@ import useSignerOrProvider from '../utils/useSignerOrProvider'; const useBuildDAOTx = () => { const signerOrProvider = useSignerOrProvider(); const { - createOptions, contracts: { fallbackHandler, votesERC20WrapperMasterCopy, @@ -32,6 +31,7 @@ const useBuildDAOTx = () => { claimingMasterCopy: erc20ClaimMasterCopy, fractalModuleMasterCopy, linearVotingMasterCopy: linearERC20VotingMasterCopy, + linearVotingERC721MasterCopy: linearERC721VotingMasterCopy, }, } = useNetworkConfig(); @@ -55,7 +55,6 @@ const useBuildDAOTx = () => { return; } const { - linearVotingERC721MasterCopyContract, fractalAzoriusMasterCopyContract, fractalModuleMasterCopyContract, multisigFreezeGuardMasterCopyContract, @@ -65,12 +64,6 @@ const useBuildDAOTx = () => { freezeERC721VotingMasterCopyContract, } = baseContracts; - if ( - createOptions.includes(GovernanceType.AZORIUS_ERC721) && - (!freezeERC721VotingMasterCopyContract || !linearVotingERC721MasterCopyContract) - ) { - return; - } if ( daoData.governance === GovernanceType.AZORIUS_ERC20 || daoData.governance === GovernanceType.AZORIUS_ERC721 @@ -81,7 +74,6 @@ const useBuildDAOTx = () => { azoriusContracts = { fractalAzoriusMasterCopyContract: fractalAzoriusMasterCopyContract.asSigner, - linearVotingERC721MasterCopyContract: linearVotingERC721MasterCopyContract.asSigner, azoriusFreezeGuardMasterCopyContract: azoriusFreezeGuardMasterCopyContract.asSigner, }; } @@ -112,6 +104,7 @@ const useBuildDAOTx = () => { erc20ClaimMasterCopy, fractalModuleMasterCopy, linearERC20VotingMasterCopy, + linearERC721VotingMasterCopy, parentAddress, parentTokenAddress, ); @@ -157,7 +150,6 @@ const useBuildDAOTx = () => { erc721LinearVotingContractAddress, dao, governance, - createOptions, fallbackHandler, votesERC20WrapperMasterCopy, votesERC20MasterCopy, @@ -170,6 +162,7 @@ const useBuildDAOTx = () => { erc20ClaimMasterCopy, fractalModuleMasterCopy, linearERC20VotingMasterCopy, + linearERC721VotingMasterCopy, ], ); diff --git a/src/hooks/DAO/useDeployAzorius.ts b/src/hooks/DAO/useDeployAzorius.ts index c67317aac2..f762d0eba5 100644 --- a/src/hooks/DAO/useDeployAzorius.ts +++ b/src/hooks/DAO/useDeployAzorius.ts @@ -37,6 +37,7 @@ const useDeployAzorius = () => { claimingMasterCopy: erc20ClaimMasterCopy, fractalModuleMasterCopy, linearVotingMasterCopy: linearERC20VotingMasterCopy, + linearVotingERC721MasterCopy: linearERC721VotingMasterCopy, }, addressPrefix, } = useNetworkConfig(); @@ -60,7 +61,6 @@ const useDeployAzorius = () => { return; } const { - linearVotingERC721MasterCopyContract, fractalAzoriusMasterCopyContract, fractalModuleMasterCopyContract, multisigFreezeGuardMasterCopyContract, @@ -73,7 +73,6 @@ const useDeployAzorius = () => { azoriusContracts = { fractalAzoriusMasterCopyContract: fractalAzoriusMasterCopyContract.asProvider, - linearVotingERC721MasterCopyContract: linearVotingERC721MasterCopyContract.asProvider, azoriusFreezeGuardMasterCopyContract: azoriusFreezeGuardMasterCopyContract.asProvider, }; @@ -103,6 +102,7 @@ const useDeployAzorius = () => { erc20ClaimMasterCopy, fractalModuleMasterCopy, linearERC20VotingMasterCopy, + linearERC721VotingMasterCopy, undefined, undefined, ); @@ -173,6 +173,7 @@ const useDeployAzorius = () => { erc20ClaimMasterCopy, fractalModuleMasterCopy, linearERC20VotingMasterCopy, + linearERC721VotingMasterCopy, ], ); diff --git a/src/hooks/safe/useSafeContracts.ts b/src/hooks/safe/useSafeContracts.ts index 1e7c86d664..ea4b0c60fb 100644 --- a/src/hooks/safe/useSafeContracts.ts +++ b/src/hooks/safe/useSafeContracts.ts @@ -5,7 +5,6 @@ import { MultisigFreezeGuard__factory, MultisigFreezeVoting__factory, Azorius__factory, - LinearERC721Voting__factory, ERC721FreezeVoting__factory, } from '@fractal-framework/fractal-contracts'; import { useMemo } from 'react'; @@ -26,7 +25,6 @@ export default function useSafeContracts() { multisigFreezeVotingMasterCopy, erc20FreezeVotingMasterCopy, erc721FreezeVotingMasterCopy, - linearVotingERC721MasterCopy, }, } = useNetworkConfig(); @@ -40,11 +38,6 @@ export default function useSafeContracts() { asProvider: Azorius__factory.connect(fractalAzoriusMasterCopy, provider), }; - const linearVotingERC721MasterCopyContract = { - asSigner: LinearERC721Voting__factory.connect(linearVotingERC721MasterCopy, signerOrProvider), - asProvider: LinearERC721Voting__factory.connect(linearVotingERC721MasterCopy, provider), - }; - const fractalModuleMasterCopyContract = { asSigner: FractalModule__factory.connect(fractalModuleMasterCopy, signerOrProvider), asProvider: FractalModule__factory.connect(fractalModuleMasterCopy, provider), @@ -89,7 +82,6 @@ export default function useSafeContracts() { freezeMultisigVotingMasterCopyContract, freezeERC20VotingMasterCopyContract, freezeERC721VotingMasterCopyContract, - linearVotingERC721MasterCopyContract, }; }, [ fractalAzoriusMasterCopy, @@ -98,7 +90,6 @@ export default function useSafeContracts() { azoriusFreezeGuardMasterCopy, multisigFreezeVotingMasterCopy, erc20FreezeVotingMasterCopy, - linearVotingERC721MasterCopy, erc721FreezeVotingMasterCopy, signerOrProvider, provider, diff --git a/src/hooks/utils/useCanUserSubmitProposal.ts b/src/hooks/utils/useCanUserSubmitProposal.ts index 00ce3bea1d..9ac47054ac 100644 --- a/src/hooks/utils/useCanUserSubmitProposal.ts +++ b/src/hooks/utils/useCanUserSubmitProposal.ts @@ -3,13 +3,13 @@ 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 useSafeContracts from '../safe/useSafeContracts'; export function useCanUserCreateProposal() { const { @@ -19,7 +19,7 @@ export function useCanUserCreateProposal() { readOnly: { user }, } = useFractal(); const safeAPI = useSafeAPI(); - const baseContracts = useSafeContracts(); + // const baseContracts = useSafeContracts(); const lookupModules = useFractalModules(); const [canUserCreateProposal, setCanUserCreateProposal] = useState(); const publicClient = usePublicClient(); @@ -40,7 +40,7 @@ export function useCanUserCreateProposal() { return !!owners?.includes(user.address || ''); }; - if (safeAddress && baseContracts) { + if (safeAddress) { const safeInfo = await safeAPI.getSafeInfo(getAddress(safeAddress)); const safeModules = await lookupModules(safeInfo.modules); const azoriusModule = getAzoriusModuleFromModules(safeModules); @@ -66,7 +66,7 @@ export function useCanUserCreateProposal() { const { owners } = safe || {}; return checkIsMultisigOwner(owners); } else if (type === GovernanceType.AZORIUS_ERC20) { - if (ozLinearVotingContractAddress && user.address && baseContracts) { + if (ozLinearVotingContractAddress) { const ozLinearVotingContract = getContract({ abi: LinearERC20VotingAbi, address: getAddress(ozLinearVotingContractAddress), @@ -74,16 +74,13 @@ export function useCanUserCreateProposal() { }); return ozLinearVotingContract.read.isProposer([getAddress(user.address)]); } - } else if ( - type === GovernanceType.AZORIUS_ERC721 && - baseContracts && - erc721LinearVotingContractAddress - ) { - const erc721LinearVotingContract = - baseContracts.linearVotingERC721MasterCopyContract.asProvider.attach( - erc721LinearVotingContractAddress, - ); - return erc721LinearVotingContract.isProposer(user.address); + } else if (type === GovernanceType.AZORIUS_ERC721 && erc721LinearVotingContractAddress) { + const erc721LinearVotingContract = getContract({ + abi: LinearERC721VotingAbi, + address: getAddress(erc721LinearVotingContractAddress), + client: publicClient, + }); + return erc721LinearVotingContract.read.isProposer([getAddress(user.address)]); } else { return; } @@ -91,7 +88,6 @@ export function useCanUserCreateProposal() { return; }, [ - baseContracts, erc721LinearVotingContractAddress, lookupModules, ozLinearVotingContractAddress, diff --git a/src/hooks/utils/useMasterCopy.ts b/src/hooks/utils/useMasterCopy.ts index 466b803ea5..4e761e6fc5 100644 --- a/src/hooks/utils/useMasterCopy.ts +++ b/src/hooks/utils/useMasterCopy.ts @@ -11,7 +11,7 @@ export function useMasterCopy() { const { getValue, setValue } = useLocalStorage(); const { baseContracts } = useFractal(); const { - contracts: { zodiacModuleProxyFactory, linearVotingMasterCopy }, + contracts: { zodiacModuleProxyFactory, linearVotingMasterCopy, linearVotingERC721MasterCopy }, } = useNetworkConfig(); const publicClient = usePublicClient(); @@ -20,9 +20,8 @@ export function useMasterCopy() { [linearVotingMasterCopy], ); const isOzLinearVotingERC721 = useCallback( - (masterCopyAddress: Address) => - masterCopyAddress === baseContracts?.linearVotingERC721MasterCopyContract.asProvider.address, - [baseContracts], + (masterCopyAddress: Address) => masterCopyAddress === linearVotingERC721MasterCopy, + [linearVotingERC721MasterCopy], ); const isMultisigFreezeGuard = useCallback( (masterCopyAddress: Address) => diff --git a/src/models/AzoriusTxBuilder.ts b/src/models/AzoriusTxBuilder.ts index 87d63762a2..c73070d48d 100644 --- a/src/models/AzoriusTxBuilder.ts +++ b/src/models/AzoriusTxBuilder.ts @@ -1,9 +1,4 @@ -import { - Azorius, - Azorius__factory, - LinearERC721Voting, - LinearERC721Voting__factory, -} from '@fractal-framework/fractal-contracts'; +import { Azorius, Azorius__factory } from '@fractal-framework/fractal-contracts'; import { getCreate2Address, Address, @@ -22,11 +17,12 @@ import { import ERC20ClaimAbi from '../assets/abi/ERC20Claim'; import GnosisSafeL2Abi from '../assets/abi/GnosisSafeL2'; import LinearERC20VotingAbi from '../assets/abi/LinearERC20Voting'; +import LinearERC721VotingAbi from '../assets/abi/LinearERC721Voting'; import ModuleProxyFactoryAbi from '../assets/abi/ModuleProxyFactory'; import VotesERC20Abi from '../assets/abi/VotesERC20'; import VotesERC20WrapperAbi from '../assets/abi/VotesERC20Wrapper'; import { SENTINEL_ADDRESS } from '../constants/common'; -import { buildContractCall, buildContractCallViem, getRandomBytes } from '../helpers'; +import { buildContractCallViem, getRandomBytes } from '../helpers'; import { BaseContracts, SafeTransaction, @@ -55,7 +51,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { public azoriusContract: Azorius | undefined; public linearERC20VotingAddress: Address | undefined; - public linearERC721VotingContract: LinearERC721Voting | undefined; + public linearERC721VotingAddress: Address | undefined; public votesTokenAddress: Address | undefined; private votesERC20WrapperMasterCopyAddress: string; @@ -64,6 +60,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { private multiSendCallOnlyAddress: Address; private erc20ClaimMasterCopyAddress: Address; private linearERC20VotingMasterCopyAddress: Address; + private linearERC721VotingMasterCopyAddress: Address; private tokenNonce: bigint; private strategyNonce: bigint; @@ -83,6 +80,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { multiSendCallOnlyAddress: Address, erc20ClaimMasterCopyAddress: Address, linearERC20VotingMasterCopyAddress: Address, + linearERC721VotingMasterCopyAddress: Address, parentAddress?: Address, parentTokenAddress?: Address, ) { @@ -109,6 +107,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { this.multiSendCallOnlyAddress = multiSendCallOnlyAddress; this.erc20ClaimMasterCopyAddress = erc20ClaimMasterCopyAddress; this.linearERC20VotingMasterCopyAddress = linearERC20VotingMasterCopyAddress; + this.linearERC721VotingMasterCopyAddress = linearERC721VotingMasterCopyAddress; if (daoData.votingStrategyType === VotingStrategyType.LINEAR_ERC20) { daoData = daoData as AzoriusERC20DAO; @@ -174,8 +173,9 @@ export class AzoriusTxBuilder extends BaseTxBuilder { false, ); } else if (daoData.votingStrategyType === VotingStrategyType.LINEAR_ERC721) { - return buildContractCall( - this.linearERC721VotingContract!, + return buildContractCallViem( + LinearERC721VotingAbi, + this.linearERC721VotingAddress!, 'setAzorius', // contract function name [this.azoriusContract!.address], 0, @@ -240,7 +240,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { [ daoData.votingStrategyType === VotingStrategyType.LINEAR_ERC20 ? this.linearERC20VotingMasterCopyAddress - : this.azoriusContracts!.linearVotingERC721MasterCopyContract.address, + : this.linearERC721VotingMasterCopyAddress, this.encodedStrategySetupData, this.strategyNonce, ], @@ -512,18 +512,14 @@ export class AzoriusTxBuilder extends BaseTxBuilder { ], ); - const encodedStrategySetupData = - this.azoriusContracts!.linearVotingERC721MasterCopyContract.interface.encodeFunctionData( - 'setUp', - [encodedStrategyInitParams], - ); - - if (!isHex(encodedStrategySetupData)) { - throw new Error('Error encoding strategy setup data'); - } + const encodedStrategySetupData = encodeFunctionData({ + abi: LinearERC721VotingAbi, + functionName: 'setUp', + args: [encodedStrategyInitParams], + }); const strategyByteCodeLinear = generateContractByteCodeLinear( - getAddress(this.azoriusContracts!.linearVotingERC721MasterCopyContract.address), + this.linearERC721VotingMasterCopyAddress, ); const strategySalt = keccak256( @@ -596,10 +592,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { this.votesTokenAddress = this.predictedTokenAddress; this.linearERC20VotingAddress = this.predictedStrategyAddress; } else if (daoData.votingStrategyType === VotingStrategyType.LINEAR_ERC721) { - this.linearERC721VotingContract = LinearERC721Voting__factory.connect( - this.predictedStrategyAddress!, - this.signerOrProvider, - ); + this.linearERC721VotingAddress = this.predictedStrategyAddress; } } } diff --git a/src/models/DaoTxBuilder.ts b/src/models/DaoTxBuilder.ts index 3f7ec7ae79..f2bbc61c17 100644 --- a/src/models/DaoTxBuilder.ts +++ b/src/models/DaoTxBuilder.ts @@ -115,8 +115,7 @@ export class DaoTxBuilder extends BaseTxBuilder { if (this.parentAddress) { const freezeGuardTxBuilder = this.txBuilderFactory.createFreezeGuardTxBuilder( azoriusTxBuilder.azoriusContract!.address, - azoriusTxBuilder.linearERC20VotingAddress ?? - azoriusTxBuilder.linearERC721VotingContract?.address, + azoriusTxBuilder.linearERC20VotingAddress ?? azoriusTxBuilder.linearERC721VotingAddress, this.parentStrategyType, this.parentStrategyAddress, ); diff --git a/src/models/TxBuilderFactory.ts b/src/models/TxBuilderFactory.ts index b08ed343d7..5fa528cd23 100644 --- a/src/models/TxBuilderFactory.ts +++ b/src/models/TxBuilderFactory.ts @@ -40,6 +40,7 @@ export class TxBuilderFactory extends BaseTxBuilder { private erc20ClaimMasterCopyAddress: string; private fractalModuleMasterCopyAddress: string; private linearERC20VotingMasterCopyAddress: string; + private linearERC721VotingMasterCopyAddress: string; constructor( signerOrProvider: ethers.Signer | any, @@ -59,6 +60,7 @@ export class TxBuilderFactory extends BaseTxBuilder { erc20ClaimMasterCopyAddress: string, fractalModuleMasterCopyAddress: string, linearERC20VotingMasterCopyAddress: string, + linearERC721VotingMasterCopyAddress: string, parentAddress?: string, parentTokenAddress?: string, ) { @@ -85,6 +87,7 @@ export class TxBuilderFactory extends BaseTxBuilder { this.erc20ClaimMasterCopyAddress = erc20ClaimMasterCopyAddress; this.fractalModuleMasterCopyAddress = fractalModuleMasterCopyAddress; this.linearERC20VotingMasterCopyAddress = linearERC20VotingMasterCopyAddress; + this.linearERC721VotingMasterCopyAddress = linearERC721VotingMasterCopyAddress; } public setSafeContract(safeAddress: Address) { @@ -190,6 +193,7 @@ export class TxBuilderFactory extends BaseTxBuilder { getAddress(this.multiSendCallOnlyAddress), getAddress(this.erc20ClaimMasterCopyAddress), getAddress(this.linearERC20VotingMasterCopyAddress), + getAddress(this.linearERC721VotingMasterCopyAddress), 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 f60c21326c..625610acca 100644 --- a/src/types/fractal.ts +++ b/src/types/fractal.ts @@ -6,7 +6,6 @@ import { MultisigFreezeVoting, MultisigFreezeGuard, ERC721FreezeVoting, - LinearERC721Voting, } from '@fractal-framework/fractal-contracts'; import { SafeMultisigTransactionWithTransfersResponse, @@ -318,7 +317,6 @@ export interface NodeHierarchy { export interface FractalContracts { fractalAzoriusMasterCopyContract: ContractConnection; - linearVotingERC721MasterCopyContract: ContractConnection; fractalModuleMasterCopyContract: ContractConnection; multisigFreezeGuardMasterCopyContract: ContractConnection; azoriusFreezeGuardMasterCopyContract: ContractConnection; diff --git a/src/types/strategyAzorius.ts b/src/types/strategyAzorius.ts index e46b3d25f5..a81fe8b6c8 100644 --- a/src/types/strategyAzorius.ts +++ b/src/types/strategyAzorius.ts @@ -1,11 +1,6 @@ -import { - Azorius, - AzoriusFreezeGuard, - LinearERC721Voting, -} from '@fractal-framework/fractal-contracts'; +import { Azorius, AzoriusFreezeGuard } from '@fractal-framework/fractal-contracts'; export interface AzoriusContracts { fractalAzoriusMasterCopyContract: Azorius; - linearVotingERC721MasterCopyContract: LinearERC721Voting; azoriusFreezeGuardMasterCopyContract: AzoriusFreezeGuard; } diff --git a/src/utils/azorius.ts b/src/utils/azorius.ts index 1f9003510b..86d58a8126 100644 --- a/src/utils/azorius.ts +++ b/src/utils/azorius.ts @@ -1,12 +1,11 @@ -import { Azorius, LinearERC721Voting } from '@fractal-framework/fractal-contracts'; +import { Azorius } from '@fractal-framework/fractal-contracts'; import { ProposalExecutedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/Azorius'; -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 LinearERC721VotingAbi from '../assets/abi/LinearERC721Voting'; import { strategyFractalProposalStates } from '../constants/strategy'; -import { logError } from '../helpers/errorLogging'; import { FractalProposalState, ProposalVotesSummary, @@ -40,7 +39,9 @@ const getQuorum = async ( erc20StrategyContract: | GetContractReturnType | undefined, - erc721StrategyContract: LinearERC721Voting | undefined, + erc721StrategyContract: + | GetContractReturnType + | undefined, strategyType: VotingStrategyType, proposalId: bigint, ) => { @@ -49,7 +50,7 @@ const getQuorum = async ( if (strategyType === VotingStrategyType.LINEAR_ERC20 && erc20StrategyContract) { quorum = await erc20StrategyContract.read.quorumVotes([Number(proposalId)]); } else if (strategyType === VotingStrategyType.LINEAR_ERC721 && erc721StrategyContract) { - quorum = (await erc721StrategyContract.quorumThreshold()).toBigInt(); + quorum = await erc721StrategyContract.read.quorumThreshold(); } else { quorum = 0n; } @@ -59,13 +60,12 @@ const getQuorum = async ( export const getProposalVotesSummary = async ( erc20Strategy: GetContractReturnType | undefined, - erc721Strategy: LinearERC721Voting | undefined, + erc721Strategy: GetContractReturnType | undefined, strategyType: VotingStrategyType, proposalId: bigint, ): Promise => { try { if (erc20Strategy !== undefined && erc721Strategy !== undefined) { - logError("we don't support multiple strategy contracts"); throw new Error("we don't support multiple strategy contracts"); } @@ -81,12 +81,14 @@ export const getProposalVotesSummary = async ( quorum: await getQuorum(erc20Strategy, erc721Strategy, strategyType, proposalId), }; } else if (erc721Strategy !== undefined) { - const { yesVotes, noVotes, abstainVotes } = await erc721Strategy.getProposalVotes(proposalId); + const [noVotes, yesVotes, abstainVotes] = await erc721Strategy.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 { @@ -101,7 +103,6 @@ export const getProposalVotesSummary = async ( // Sometimes loading DAO proposals called in the moment when proposal was **just** created // Thus, calling `getProposalVotes` for such a proposal reverts with error // This helps to prevent such case, while event listeners still should get proper data - logError('Error while retrieving Azorius proposal votes', e); return { yes: 0n, no: 0n, @@ -113,12 +114,11 @@ export const getProposalVotesSummary = async ( const getProposalVotes = ( erc20VotedEvents: GetContractEventsReturnType | undefined, - erc721VotedEvents: ERC721VotedEvent[] | undefined, + erc721VotedEvents: GetContractEventsReturnType | undefined, proposalId: bigint, ): (ProposalVote | ERC721ProposalVote)[] => { if (erc20VotedEvents !== undefined && erc721VotedEvents !== undefined) { - logError("two voting contracts? we don't support that."); - return []; + throw new Error("two voting contracts? we don't support that."); } if (erc20VotedEvents !== undefined) { @@ -140,17 +140,24 @@ const getProposalVotes = ( } return events; } else if (erc721VotedEvents !== undefined) { - const erc721ProposalVoteEvents = erc721VotedEvents.filter( - voteEvent => proposalId === BigInt(voteEvent.args.proposalId), - ); + const erc721ProposalVoteEvents = erc721VotedEvents.filter(voteEvent => { + if (!voteEvent.args.proposalId) return false; + return proposalId === BigInt(voteEvent.args.proposalId); + }); - return erc721ProposalVoteEvents.map(({ args: { voter, voteType, tokenIds, ...rest } }) => ({ - ...rest, - voter, - choice: VOTE_CHOICES[voteType], - weight: 1n, - tokenIds: tokenIds.map(id => id.toString()), - })); + const events = []; + for (const event of erc721ProposalVoteEvents) { + if (!event.args.voteType || !event.args.voter || !event.args.tokenIds) { + continue; + } + events.push({ + voter: event.args.voter, + choice: VOTE_CHOICES[event.args.voteType], + weight: 1n, + tokenIds: event.args.tokenIds.map(id => id.toString()), + }); + } + return events; } return []; @@ -160,7 +167,9 @@ export const mapProposalCreatedEventToProposal = async ( erc20StrategyContract: | GetContractReturnType | undefined, - erc721StrategyContract: LinearERC721Voting | undefined, + erc721StrategyContract: + | GetContractReturnType + | undefined, strategyType: VotingStrategyType, proposalId: bigint, proposer: string, @@ -169,12 +178,13 @@ export const mapProposalCreatedEventToProposal = async ( erc20VotedEvents: Promise< GetContractEventsReturnType | undefined >, - erc721VotedEvents: Promise, + erc721VotedEvents: Promise< + GetContractEventsReturnType | undefined + >, executedEvents: Promise, data?: ProposalData, ) => { if (erc20StrategyContract !== undefined && erc721StrategyContract !== undefined) { - logError("we don't support multiple strategy contracts"); throw new Error("we don't support multiple strategy contracts"); } @@ -196,14 +206,15 @@ export const mapProposalCreatedEventToProposal = async ( proposalVotes.startBlock = stratProposalVotes[3]; proposalVotes.endBlock = stratProposalVotes[4]; } else if (erc721StrategyContract !== undefined) { - 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(); + const stratProposalVotes = await erc721StrategyContract.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 { - logError('we need a strat!'); throw new Error('we need a strategy!'); }