diff --git a/src/assets/abi/IVotes.ts b/src/assets/abi/IVotes.ts new file mode 100644 index 0000000000..6d52b2eda1 --- /dev/null +++ b/src/assets/abi/IVotes.ts @@ -0,0 +1,186 @@ +const IVotesAbi = [ + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'delegator', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'fromDelegate', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'toDelegate', + type: 'address', + }, + ], + name: 'DelegateChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'delegate', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'previousBalance', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'newBalance', + type: 'uint256', + }, + ], + name: 'DelegateVotesChanged', + type: 'event', + }, + { + inputs: [ + { + internalType: 'address', + name: 'delegatee', + type: 'address', + }, + ], + name: 'delegate', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'delegatee', + type: 'address', + }, + { + internalType: 'uint256', + name: 'nonce', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'expiry', + type: 'uint256', + }, + { + internalType: 'uint8', + name: 'v', + type: 'uint8', + }, + { + internalType: 'bytes32', + name: 'r', + type: 'bytes32', + }, + { + internalType: 'bytes32', + name: 's', + type: 'bytes32', + }, + ], + name: 'delegateBySig', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'delegates', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'blockNumber', + type: 'uint256', + }, + ], + name: 'getPastTotalSupply', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + { + internalType: 'uint256', + name: 'blockNumber', + type: 'uint256', + }, + ], + name: 'getPastVotes', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'getVotes', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const; + +export default IVotesAbi; diff --git a/src/components/DaoCreator/hooks/usePrepareFormData.ts b/src/components/DaoCreator/hooks/usePrepareFormData.ts index 4e1e71fcea..7e6d2db3b4 100644 --- a/src/components/DaoCreator/hooks/usePrepareFormData.ts +++ b/src/components/DaoCreator/hooks/usePrepareFormData.ts @@ -1,6 +1,8 @@ -import { IVotes__factory } from '@fractal-framework/fractal-contracts'; import { useCallback } from 'react'; -import { getAddress } from 'viem'; +import { getAddress, getContract } from 'viem'; +import { usePublicClient } from 'wagmi'; +import IVotesAbi from '../../../assets/abi/IVotes'; +import { SENTINEL_ADDRESS } from '../../../constants/common'; import { useEthersProvider } from '../../../providers/Ethers/hooks/useEthersProvider'; import { useEthersSigner } from '../../../providers/Ethers/hooks/useEthersSigner'; import { @@ -20,6 +22,8 @@ export function usePrepareFormData() { const signer = useEthersSigner(); const provider = useEthersProvider(); + const publicClient = usePublicClient(); + // Helper function to prepare freezeGuard data const prepareFreezeGuardData = useCallback( async ( @@ -52,18 +56,24 @@ export function usePrepareFormData() { const checkVotesToken = useCallback( async (address: string) => { - if (provider) { + if (publicClient) { try { - const votesContract = IVotes__factory.connect(address, provider); - await votesContract.delegates('0x0000000000000000000000000000000000000001'); - await votesContract.getVotes('0x0000000000000000000000000000000000000001'); + const votesContract = getContract({ + abi: IVotesAbi, + address: getAddress(address), + client: publicClient, + }); + await Promise.all([ + votesContract.read.delegates([SENTINEL_ADDRESS]), + votesContract.read.getVotes([SENTINEL_ADDRESS]), + ]); return true; } catch (error) { return false; } } }, - [provider], + [publicClient], ); const prepareMultisigFormData = useCallback( diff --git a/src/components/pages/DaoSettings/components/Signers/modals/RemoveSignerModal.tsx b/src/components/pages/DaoSettings/components/Signers/modals/RemoveSignerModal.tsx index 77352af879..fd3af8ff2b 100644 --- a/src/components/pages/DaoSettings/components/Signers/modals/RemoveSignerModal.tsx +++ b/src/components/pages/DaoSettings/components/Signers/modals/RemoveSignerModal.tsx @@ -14,6 +14,7 @@ import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Address } from 'viem'; import { useEnsName } from 'wagmi'; +import { SENTINEL_ADDRESS } from '../../../../../../constants/common'; import { useFractal } from '../../../../../../providers/App/AppProvider'; import { useNetworkConfig } from '../../../../../../providers/NetworkConfig/NetworkConfigProvider'; import SupportTooltip from '../../../../../ui/badges/SupportTooltip'; @@ -65,9 +66,7 @@ function RemoveSignerModal({ useEffect(() => { const signerIndex = signers.findIndex(signer => signer === selectedSigner); - setPrevSigner( - signerIndex > 0 ? signers[signerIndex - 1] : '0x0000000000000000000000000000000000000001', - ); + setPrevSigner(signerIndex > 0 ? signers[signerIndex - 1] : SENTINEL_ADDRESS); }, [selectedSigner, signers]); return ( diff --git a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx index 016f81b5ba..fc3aa86ec5 100644 --- a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx +++ b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx @@ -3,6 +3,7 @@ import { ERC20FreezeVoting, MultisigFreezeVoting } from '@fractal-framework/frac import { useMemo, useCallback, useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { Address, getAddress } from 'viem'; +import { SENTINEL_ADDRESS } from '../../../../constants/common'; import { DAO_ROUTES } from '../../../../constants/routes'; import { isWithinFreezePeriod, @@ -91,10 +92,7 @@ export function ManageDAOMenu({ // @dev assumes the first strategy is the voting contract const votingContractAddress = ( - await azoriusContract.asProvider.getStrategies( - '0x0000000000000000000000000000000000000001', - 0, - ) + await azoriusContract.asProvider.getStrategies(SENTINEL_ADDRESS, 0) )[1]; const masterCopyData = await getZodiacModuleProxyMasterCopyData( getAddress(votingContractAddress), diff --git a/src/constants/common.ts b/src/constants/common.ts index c50e79f836..a9117f7558 100644 --- a/src/constants/common.ts +++ b/src/constants/common.ts @@ -7,3 +7,4 @@ export const BACKGROUND_SEMI_TRANSPARENT = 'black.900-semi-transparent'; */ export const TOOLTIP_MAXW = '18rem'; export const ADDRESS_MULTISIG_METADATA = '0xdA00000000000000000000000000000000000Da0'; +export const SENTINEL_ADDRESS = '0x0000000000000000000000000000000000000001'; diff --git a/src/hooks/DAO/loaders/governance/useERC20LinearToken.ts b/src/hooks/DAO/loaders/governance/useERC20LinearToken.ts index 7603078425..735c0acca1 100644 --- a/src/hooks/DAO/loaders/governance/useERC20LinearToken.ts +++ b/src/hooks/DAO/loaders/governance/useERC20LinearToken.ts @@ -1,4 +1,3 @@ -import { DelegateChangedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/VotesERC20'; import { useCallback, useEffect, useRef } from 'react'; import { getAddress } from 'viem'; import { useFractal } from '../../../../providers/App/AppProvider'; @@ -76,20 +75,10 @@ export const useERC20LinearToken = ({ onMount = true }: { onMount?: boolean }) = (await tokenContract.getVotes(account)).toBigInt(), ]); - let delegateChangeEvents: DelegateChangedEvent[]; - try { - delegateChangeEvents = await tokenContract.queryFilter( - tokenContract.filters.DelegateChanged(), - ); - } catch (e) { - delegateChangeEvents = []; - } - const tokenAccountData = { balance: tokenBalance, delegatee: tokenDelegatee, votingWeight: tokenVotingWeight, - isDelegatesSet: delegateChangeEvents.length > 0, }; action.dispatch({ diff --git a/src/hooks/DAO/loaders/governance/useERC721Tokens.ts b/src/hooks/DAO/loaders/governance/useERC721Tokens.ts index f857d5ed22..d2060b2741 100644 --- a/src/hooks/DAO/loaders/governance/useERC721Tokens.ts +++ b/src/hooks/DAO/loaders/governance/useERC721Tokens.ts @@ -1,22 +1,20 @@ -import { ERC721__factory } from '@fractal-framework/fractal-contracts'; import { useCallback } from 'react'; -import { getAddress, zeroAddress } from 'viem'; -import { logError } from '../../../../helpers/errorLogging'; +import { erc721Abi, getAddress, getContract, zeroAddress } from 'viem'; +import { usePublicClient } from 'wagmi'; import { useFractal } from '../../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; import { ERC721TokenData } from '../../../../types'; import useSafeContracts from '../../../safe/useSafeContracts'; -import useSignerOrProvider from '../../../utils/useSignerOrProvider'; export default function useERC721Tokens() { - const signerOrProvider = useSignerOrProvider(); const { governanceContracts: { erc721LinearVotingContractAddress }, action, } = useFractal(); const baseContracts = useSafeContracts(); + const publicClient = usePublicClient(); const loadERC721Tokens = useCallback(async () => { - if (!erc721LinearVotingContractAddress || !signerOrProvider || !baseContracts) { + if (!erc721LinearVotingContractAddress || !baseContracts || !publicClient) { return; } const erc721LinearVotingContract = @@ -26,22 +24,19 @@ export default function useERC721Tokens() { const addresses = await erc721LinearVotingContract.getAllTokenAddresses(); const erc721Tokens: ERC721TokenData[] = await Promise.all( addresses.map(async address => { - const tokenContract = ERC721__factory.connect(address, signerOrProvider); + const tokenContract = getContract({ + abi: erc721Abi, + address: getAddress(address), + client: publicClient, + }); const votingWeight = (await erc721LinearVotingContract.getTokenWeight(address)).toBigInt(); - const name = await tokenContract.name(); - const symbol = await tokenContract.symbol(); - let totalSupply = undefined; - try { - const tokenMintEvents = await tokenContract.queryFilter( - tokenContract.filters.Transfer(zeroAddress, null), - ); - const tokenBurnEvents = await tokenContract.queryFilter( - tokenContract.filters.Transfer(null, zeroAddress), - ); - totalSupply = BigInt(tokenMintEvents.length - tokenBurnEvents.length); - } catch (e) { - logError('Error while getting ERC721 total supply'); - } + const [name, symbol, tokenMintEvents, tokenBurnEvents] = await Promise.all([ + tokenContract.read.name(), + tokenContract.read.symbol(), + tokenContract.getEvents.Transfer({ from: zeroAddress }), + tokenContract.getEvents.Transfer({ to: zeroAddress }), + ]); + const totalSupply = BigInt(tokenMintEvents.length - tokenBurnEvents.length); return { name, symbol, address: getAddress(address), votingWeight, totalSupply }; }), ); @@ -50,7 +45,7 @@ export default function useERC721Tokens() { type: FractalGovernanceAction.SET_ERC721_TOKENS_DATA, payload: erc721Tokens, }); - }, [erc721LinearVotingContractAddress, signerOrProvider, action, baseContracts]); + }, [action, baseContracts, erc721LinearVotingContractAddress, publicClient]); return loadERC721Tokens; } diff --git a/src/hooks/DAO/loaders/governance/useLockRelease.ts b/src/hooks/DAO/loaders/governance/useLockRelease.ts index 8f3b4305c9..e15a5c7fc6 100644 --- a/src/hooks/DAO/loaders/governance/useLockRelease.ts +++ b/src/hooks/DAO/loaders/governance/useLockRelease.ts @@ -1,4 +1,3 @@ -import { DelegateChangedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/VotesERC20'; import { useCallback, useEffect, useRef } from 'react'; import { LockRelease__factory } from '../../../../assets/typechain-types/dcnt'; import { useFractal } from '../../../../providers/App/AppProvider'; @@ -35,20 +34,10 @@ export const useLockRelease = ({ onMount = true }: { onMount?: boolean }) => { (await lockReleaseContract.getVotes(account)).toBigInt(), ]); - let delegateChangeEvents: DelegateChangedEvent[]; - try { - delegateChangeEvents = await lockReleaseContract.queryFilter( - lockReleaseContract.filters.DelegateChanged(), - ); - } catch (e) { - delegateChangeEvents = []; - } - const tokenAccountData = { balance: tokenAmountTotal - tokenAmountReleased, delegatee: tokenDelegatee, votingWeight: tokenVotingWeight, - isDelegatesSet: delegateChangeEvents.length > 0, }; action.dispatch({ type: DecentGovernanceAction.SET_LOCKED_TOKEN_ACCOUNT_DATA, diff --git a/src/hooks/DAO/loaders/useGovernanceContracts.ts b/src/hooks/DAO/loaders/useGovernanceContracts.ts index 378f8e6272..d5c7d13e65 100644 --- a/src/hooks/DAO/loaders/useGovernanceContracts.ts +++ b/src/hooks/DAO/loaders/useGovernanceContracts.ts @@ -4,6 +4,7 @@ import { Address, getContract, getAddress } from 'viem'; import { usePublicClient } from 'wagmi'; import VotesERC20WrapperAbi from '../../../assets/abi/VotesERC20Wrapper'; import { LockRelease__factory } from '../../../assets/typechain-types/dcnt'; +import { SENTINEL_ADDRESS } from '../../../constants/common'; import { useFractal } from '../../../providers/App/AppProvider'; import { GovernanceContractAction } from '../../../providers/App/governanceContracts/action'; import { getAzoriusModuleFromModules } from '../../../utils'; @@ -42,9 +43,7 @@ export const useGovernanceContracts = () => { let lockReleaseContractAddress: string | undefined; // @dev assumes the first strategy is the voting contract - const votingStrategyAddress = ( - await azoriusContract.getStrategies('0x0000000000000000000000000000000000000001', 0) - )[1]; + const votingStrategyAddress = (await azoriusContract.getStrategies(SENTINEL_ADDRESS, 0))[1]; const masterCopyData = await getZodiacModuleProxyMasterCopyData( getAddress(votingStrategyAddress), diff --git a/src/hooks/DAO/proposal/useSubmitProposal.ts b/src/hooks/DAO/proposal/useSubmitProposal.ts index 972343f2c8..a62096c0e5 100644 --- a/src/hooks/DAO/proposal/useSubmitProposal.ts +++ b/src/hooks/DAO/proposal/useSubmitProposal.ts @@ -6,7 +6,7 @@ import { useCallback, useMemo, useState } from 'react'; import { toast } from 'react-toastify'; import { isAddress, getAddress, encodeAbiParameters, parseAbiParameters, isHex } from 'viem'; import { GnosisSafeL2__factory } from '../../../assets/typechain-types/usul/factories/@gnosis.pm/safe-contracts/contracts'; -import { ADDRESS_MULTISIG_METADATA } from '../../../constants/common'; +import { ADDRESS_MULTISIG_METADATA, SENTINEL_ADDRESS } from '../../../constants/common'; import { buildSafeAPIPost, encodeMultiSend } from '../../../helpers'; import { logError } from '../../../helpers/errorLogging'; import { useFractal } from '../../../providers/App/AppProvider'; @@ -297,10 +297,7 @@ export default function useSubmitProposal() { const azoriusModuleContract = azoriusModule.moduleContract as Azorius; // @dev assumes the first strategy is the voting contract const votingStrategyAddress = ( - await azoriusModuleContract.getStrategies( - '0x0000000000000000000000000000000000000001', - 0, - ) + await azoriusModuleContract.getStrategies(SENTINEL_ADDRESS, 0) )[1]; submitAzoriusProposal({ proposalData, diff --git a/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts b/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts index 02f127e5a5..7c752b6a22 100644 --- a/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts +++ b/src/hooks/DAO/proposal/useUserERC721VotingTokens.ts @@ -1,12 +1,12 @@ import { - ERC721__factory, Azorius, LinearERC721Voting__factory, LinearERC721Voting, } from '@fractal-framework/fractal-contracts'; import { useState, useEffect, useCallback } from 'react'; -import { getAddress } from 'viem'; -import { logError } from '../../../helpers/errorLogging'; +import { erc721Abi, getAddress, getContract } from 'viem'; +import { usePublicClient } from 'wagmi'; +import { SENTINEL_ADDRESS } from '../../../constants/common'; import { useFractal } from '../../../providers/App/AppProvider'; import { useSafeAPI } from '../../../providers/App/hooks/useSafeAPI'; import { AzoriusGovernance } from '../../../types'; @@ -45,6 +45,7 @@ export default function useUserERC721VotingTokens( const baseContracts = useSafeContracts(); const lookupModules = useFractalModules(); const safeAPI = useSafeAPI(); + const publicClient = usePublicClient(); const azoriusGovernance = governance as AzoriusGovernance; const { erc721Tokens } = azoriusGovernance; @@ -60,7 +61,7 @@ export default function useUserERC721VotingTokens( let govTokens = erc721Tokens; let votingContract: LinearERC721Voting | undefined; - if (!baseContracts || !signerOrProvider || !daoAddress) { + if (!baseContracts || !signerOrProvider || !daoAddress || !publicClient || !safeAPI) { return { totalVotingTokenAddresses: totalTokenAddresses, totalVotingTokenIds: totalTokenIds, @@ -71,14 +72,14 @@ export default function useUserERC721VotingTokens( if (_safeAddress && daoAddress !== _safeAddress) { // Means getting these for any safe, primary use case - calculating user voting weight for freeze voting - const safeInfo = await safeAPI!.getSafeInfo(getAddress(_safeAddress)); + const safeInfo = await safeAPI.getSafeInfo(getAddress(_safeAddress)); const safeModules = await lookupModules(safeInfo.modules); const azoriusModule = getAzoriusModuleFromModules(safeModules); if (azoriusModule && azoriusModule.moduleContract) { const azoriusContract = azoriusModule.moduleContract as Azorius; // @dev assumes the first strategy is the voting contract const votingContractAddress = ( - await azoriusContract.getStrategies('0x0000000000000000000000000000000000000001', 0) + await azoriusContract.getStrategies(SENTINEL_ADDRESS, 0) )[1]; votingContract = LinearERC721Voting__factory.connect( votingContractAddress, @@ -87,24 +88,24 @@ export default function useUserERC721VotingTokens( const addresses = await votingContract.getAllTokenAddresses(); govTokens = await Promise.all( addresses.map(async tokenAddress => { - const tokenContract = ERC721__factory.connect(tokenAddress, signerOrProvider); - const votingWeight = (await votingContract!.getTokenWeight(tokenAddress)).toBigInt(); - const name = await tokenContract.name(); - const symbol = await tokenContract.symbol(); - let totalSupply = undefined; - try { - const tokenSentEvents = await tokenContract.queryFilter( - tokenContract.filters.Transfer(null, null), - ); - totalSupply = BigInt(tokenSentEvents.length); - } catch (e) { - logError('Error while getting ERC721 total supply'); - } - return { name, symbol, address: getAddress(tokenAddress), votingWeight, totalSupply }; + const tokenContract = getContract({ + abi: erc721Abi, + address: getAddress(tokenAddress), + client: publicClient, + }); + + const [votingWeight, name, symbol] = await Promise.all([ + (await votingContract!.getTokenWeight(tokenAddress)).toBigInt(), + tokenContract.read.name(), + tokenContract.read.symbol(), + ]); + + return { name, symbol, address: getAddress(tokenAddress), votingWeight }; }), ); } } + if (erc721LinearVotingContractAddress && !votingContract) { votingContract = baseContracts.linearVotingERC721MasterCopyContract.asProvider.attach( erc721LinearVotingContractAddress, @@ -119,29 +120,36 @@ export default function useUserERC721VotingTokens( remainingTokenIds: tokenIds, }; } + + const userAddress = getAddress(user.address); await Promise.all( // Using `map` instead of `forEach` to simplify usage of `Promise.all` // and guarantee syncronous contractFn assignment govTokens.map(async token => { - const tokenContract = ERC721__factory.connect(token.address, signerOrProvider!); - if ((await tokenContract.balanceOf(user.address!)).toBigInt() > 0n) { - const tokenSentEvents = await tokenContract.queryFilter( - tokenContract.filters.Transfer(user.address!, null), - ); - const tokenReceivedEvents = await tokenContract.queryFilter( - tokenContract.filters.Transfer(null, user.address), - ); + const tokenContract = getContract({ + abi: erc721Abi, + address: token.address, + client: publicClient, + }); + if ((await tokenContract.read.balanceOf([userAddress])) > 0n) { + const tokenSentEvents = await tokenContract.getEvents.Transfer({ from: userAddress }); + const tokenReceivedEvents = await tokenContract.getEvents.Transfer({ to: userAddress }); + const allEvents = tokenSentEvents .concat(tokenReceivedEvents) .sort( - (a, b) => a.blockNumber - b.blockNumber || a.transactionIndex - b.transactionIndex, + (a, b) => + Number(a.blockNumber - b.blockNumber) || a.transactionIndex - b.transactionIndex, ); const ownedTokenIds = new Set(); allEvents.forEach(({ args: { to, from, tokenId } }) => { - if (to.toLowerCase() === user.address!.toLowerCase()) { + if (!to || !from || !tokenId) { + throw new Error('An ERC721 event has undefiend data'); + } + if (to.toLowerCase() === userAddress.toLowerCase()) { ownedTokenIds.add(tokenId.toString()); - } else if (from.toLowerCase() === user.address!.toLowerCase()) { + } else if (from.toLowerCase() === userAddress.toLowerCase()) { ownedTokenIds.delete(tokenId.toString()); } }); @@ -191,14 +199,15 @@ export default function useUserERC721VotingTokens( }; }, [ + baseContracts, + daoAddress, erc721LinearVotingContractAddress, erc721Tokens, - signerOrProvider, lookupModules, + publicClient, safeAPI, - daoAddress, + signerOrProvider, user.address, - baseContracts, ], ); diff --git a/src/hooks/DAO/useClawBack.ts b/src/hooks/DAO/useClawBack.ts index 88012260af..a4d29c3542 100644 --- a/src/hooks/DAO/useClawBack.ts +++ b/src/hooks/DAO/useClawBack.ts @@ -1,7 +1,15 @@ -import { ERC20__factory, FractalModule } from '@fractal-framework/fractal-contracts'; +import { FractalModule } from '@fractal-framework/fractal-contracts'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { Address, encodeAbiParameters, getAddress, isHex, parseAbiParameters } from 'viem'; +import { + Address, + encodeAbiParameters, + encodeFunctionData, + erc20Abi, + getAddress, + isHex, + parseAbiParameters, +} from 'viem'; import { useSafeAPI } from '../../providers/App/hooks/useSafeAPI'; import { useEthersProvider } from '../../providers/Ethers/hooks/useEthersProvider'; import { FractalModuleType, FractalNode } from '../../types'; @@ -54,14 +62,11 @@ export default function useClawBack({ childSafeInfo, parentAddress }: IUseClawBa calldata: fractalModuleCalldata, }; } else { - const tokenContract = ERC20__factory.connect(asset.tokenAddress, provider); - const clawBackCalldata = tokenContract.interface.encodeFunctionData('transfer', [ - parentAddress, - asset.balance, - ]); - if (!isHex(clawBackCalldata)) { - throw new Error('Error encoding clawback call data'); - } + const clawBackCalldata = encodeFunctionData({ + abi: erc20Abi, + functionName: 'transfer', + args: [parentAddress, BigInt(asset.balance)], + }); const txData = encodeAbiParameters( parseAbiParameters('address, uint256, bytes, uint8'), [getAddress(asset.tokenAddress), 0n, clawBackCalldata, 0], diff --git a/src/hooks/utils/useCanUserSubmitProposal.ts b/src/hooks/utils/useCanUserSubmitProposal.ts index 61bdf2d7db..20868d584b 100644 --- a/src/hooks/utils/useCanUserSubmitProposal.ts +++ b/src/hooks/utils/useCanUserSubmitProposal.ts @@ -1,6 +1,7 @@ import { Azorius } from '@fractal-framework/fractal-contracts'; import { useState, useCallback, useEffect } from 'react'; import { getAddress } from 'viem'; +import { SENTINEL_ADDRESS } from '../../constants/common'; import { useFractal } from '../../providers/App/AppProvider'; import { useSafeAPI } from '../../providers/App/hooks/useSafeAPI'; import { GovernanceType } from '../../types'; @@ -44,7 +45,7 @@ export function useCanUserCreateProposal() { const azoriusContract = azoriusModule.moduleContract as Azorius; // @dev assumes the first strategy is the voting contract const votingContractAddress = ( - await azoriusContract.getStrategies('0x0000000000000000000000000000000000000001', 0) + await azoriusContract.getStrategies(SENTINEL_ADDRESS, 0) )[1]; const votingContract = baseContracts.linearVotingMasterCopyContract.asProvider.attach(votingContractAddress); diff --git a/src/models/AzoriusTxBuilder.ts b/src/models/AzoriusTxBuilder.ts index ca4a7658b3..798526ed63 100644 --- a/src/models/AzoriusTxBuilder.ts +++ b/src/models/AzoriusTxBuilder.ts @@ -23,6 +23,7 @@ import { } from 'viem'; import VotesERC20WrapperAbi from '../assets/abi/VotesERC20Wrapper'; import { GnosisSafeL2 } from '../assets/typechain-types/usul/@gnosis.pm/safe-contracts/contracts'; +import { SENTINEL_ADDRESS } from '../constants/common'; import { buildContractCall, getRandomBytes } from '../helpers'; import { BaseContracts, @@ -424,7 +425,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { [ getAddress(this.safeContract.address), // owner getAddress(this.predictedTokenAddress), // governance token - '0x0000000000000000000000000000000000000001', // Azorius module + SENTINEL_ADDRESS, // Azorius module Number(azoriusGovernanceDaoData.votingPeriod), 1n, // proposer weight, how much is needed to create a proposal. (azoriusGovernanceDaoData.quorumPercentage * quorumDenominator) / 100n, // quorom numerator, denominator is 1,000,000, so quorum percentage is quorumNumerator * 100 / quorumDenominator @@ -470,7 +471,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { getAddress(this.safeContract.address), // owner daoData.nfts.map(nft => nft.tokenAddress!), // governance tokens addresses daoData.nfts.map(nft => nft.tokenWeight), // governance tokens weights - '0x0000000000000000000000000000000000000001', // Azorius module + SENTINEL_ADDRESS, // Azorius module Number(azoriusGovernanceDaoData.votingPeriod), daoData.quorumThreshold, // quorom threshold. Since smart contract can't know total of NFTs minted - we need to provide it manually 1n, // proposer weight, how much is needed to create a proposal. diff --git a/src/providers/App/governance/reducer.ts b/src/providers/App/governance/reducer.ts index 6a3216f691..e4473120db 100644 --- a/src/providers/App/governance/reducer.ts +++ b/src/providers/App/governance/reducer.ts @@ -25,7 +25,6 @@ export const initialVotesTokenAccountData = { balance: null, delegatee: null, votingWeight: null, - isDelegatesSet: null, }; export const governanceReducer = (state: FractalGovernance, action: FractalGovernanceActions) => { diff --git a/src/providers/App/useReadOnlyValues.ts b/src/providers/App/useReadOnlyValues.ts index badd7c25b5..689ba9227d 100644 --- a/src/providers/App/useReadOnlyValues.ts +++ b/src/providers/App/useReadOnlyValues.ts @@ -1,8 +1,7 @@ -import { ERC721__factory } from '@fractal-framework/fractal-contracts'; import * as Sentry from '@sentry/react'; import { useEffect, useState, useCallback } from 'react'; -import { getAddress } from 'viem'; -import useSignerOrProvider from '../../hooks/utils/useSignerOrProvider'; +import { erc721Abi, getAddress, getContract } from 'viem'; +import { usePublicClient } from 'wagmi'; import { ReadOnlyState, Fractal, @@ -26,7 +25,7 @@ const INITIAL_READ_ONLY_VALUES: ReadOnlyState = { */ export const useReadOnlyValues = ({ node, governance }: Fractal, _account?: string) => { const [readOnlyValues, setReadOnlyValues] = useState(INITIAL_READ_ONLY_VALUES); - const signerOrProvider = useSignerOrProvider(); + const publicClient = usePublicClient(); const loadReadOnlyValues = useCallback(async () => { const getVotingWeight = async () => { @@ -40,14 +39,18 @@ export const useReadOnlyValues = ({ node, governance }: Fractal, _account?: stri const tokenWeight = azoriusGovernance.votesToken?.votingWeight || 0n; return lockedTokenWeight || tokenWeight; case GovernanceType.AZORIUS_ERC721: - if (!_account || !azoriusGovernance.erc721Tokens || !signerOrProvider) { + if (!_account || !azoriusGovernance.erc721Tokens || !publicClient) { return 0n; } const userVotingWeight = ( await Promise.all( azoriusGovernance.erc721Tokens.map(async ({ address, votingWeight }) => { - const tokenContract = ERC721__factory.connect(address, signerOrProvider); - const userBalance = (await tokenContract.balanceOf(_account)).toBigInt(); + const tokenContract = getContract({ + abi: erc721Abi, + address: address, + client: publicClient, + }); + const userBalance = await tokenContract.read.balanceOf([getAddress(_account)]); return userBalance * votingWeight; }), ) @@ -74,7 +77,7 @@ export const useReadOnlyValues = ({ node, governance }: Fractal, _account?: stri governance.type === GovernanceType.AZORIUS_ERC721, }, }); - }, [node, governance, _account, signerOrProvider]); + }, [node, governance, _account, publicClient]); useEffect(() => { loadReadOnlyValues(); }, [loadReadOnlyValues]); diff --git a/src/types/account.ts b/src/types/account.ts index bcda8362e0..ecd0c2a430 100644 --- a/src/types/account.ts +++ b/src/types/account.ts @@ -5,7 +5,6 @@ export interface VotesData { balance: bigint | null; delegatee: string | null; votingWeight: bigint | null; - isDelegatesSet: boolean | null; } export type UnderlyingTokenData = Omit< ERC20TokenData, diff --git a/src/types/contract.ts b/src/types/contract.ts index 59c1b54b0b..5947349e24 100644 --- a/src/types/contract.ts +++ b/src/types/contract.ts @@ -1,16 +1,11 @@ import { GnosisSafeProxyFactory, ModuleProxyFactory, - Azorius, - LinearERC20Voting, FractalModule, FractalRegistry, MultisigFreezeGuard, - AzoriusFreezeGuard, MultisigFreezeVoting, ERC20FreezeVoting, - VotesERC20, - ERC20Claim, KeyValuePairs, ERC721FreezeVoting, } from '@fractal-framework/fractal-contracts'; @@ -26,24 +21,6 @@ export type ContractConnection = { asProvider: T; }; -export interface DAOContracts { - multiSendContract: ContractConnection; - safeFactoryContract: ContractConnection; - fractalAzoriusMasterCopyContract: ContractConnection; - linearVotingMasterCopyContract: ContractConnection; - safeSingletonContract: ContractConnection; - zodiacModuleProxyFactoryContract: ContractConnection; - fractalModuleMasterCopyContract: ContractConnection; - fractalRegistryContract: ContractConnection; - multisigFreezeGuardMasterCopyContract: ContractConnection; - azoriusFreezeGuardMasterCopyContract: ContractConnection; - freezeMultisigVotingMasterCopyContract: ContractConnection; - freezeERC20VotingMasterCopyContract: ContractConnection; - votesTokenMasterCopyContract: ContractConnection; - claimingMasterCopyContract: ContractConnection; - keyValuePairsContract: ContractConnection; -} - export interface BaseContracts { fractalModuleMasterCopyContract: FractalModule; fractalRegistryContract: FractalRegistry; diff --git a/src/types/fractal.ts b/src/types/fractal.ts index 4b71576af9..798238f5f3 100644 --- a/src/types/fractal.ts +++ b/src/types/fractal.ts @@ -182,7 +182,6 @@ export interface ITokenAccount { delegatee: string | undefined; votingWeight?: bigint; votingWeightString: string | undefined; - isDelegatesSet: boolean | undefined; } /**