diff --git a/src/assets/abi/GnosisSafeL2.ts b/src/assets/abi/GnosisSafeL2.ts index 95b0bf23e0..fa2eb254ae 100644 --- a/src/assets/abi/GnosisSafeL2.ts +++ b/src/assets/abi/GnosisSafeL2.ts @@ -1,4 +1,4 @@ -export default [ +const GnosisSafeL2Abi = [ { anonymous: false, inputs: [ @@ -1135,4 +1135,6 @@ export default [ stateMutability: 'payable', type: 'receive', }, -]; +] as const; + +export default GnosisSafeL2Abi; diff --git a/src/assets/abi/VotesERC20Wrapper.ts b/src/assets/abi/VotesERC20Wrapper.ts new file mode 100644 index 0000000000..99b9c97b91 --- /dev/null +++ b/src/assets/abi/VotesERC20Wrapper.ts @@ -0,0 +1,818 @@ +const VotesERC20Wrapper = [ + { + inputs: [], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'Approval', + type: 'event', + }, + { + 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', + }, + { + 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: 'uint256', + name: 'id', + type: 'uint256', + }, + ], + name: 'Snapshot', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'from', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'to', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'Transfer', + type: 'event', + }, + { + inputs: [], + name: 'DOMAIN_SEPARATOR', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + ], + name: 'allowance', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'approve', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'balanceOf', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + { + internalType: 'uint256', + name: 'snapshotId', + type: 'uint256', + }, + ], + name: 'balanceOfAt', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'captureSnapShot', + outputs: [ + { + internalType: 'uint256', + name: 'snapId', + type: 'uint256', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + { + internalType: 'uint32', + name: 'pos', + type: 'uint32', + }, + ], + name: 'checkpoints', + outputs: [ + { + components: [ + { + internalType: 'uint32', + name: 'fromBlock', + type: 'uint32', + }, + { + internalType: 'uint224', + name: 'votes', + type: 'uint224', + }, + ], + internalType: 'struct ERC20VotesUpgradeable.Checkpoint', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'decimals', + outputs: [ + { + internalType: 'uint8', + name: '', + type: 'uint8', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + internalType: 'uint256', + name: 'subtractedValue', + type: 'uint256', + }, + ], + name: 'decreaseAllowance', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + 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: 'address', + name: 'account', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'depositFor', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + 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', + }, + { + inputs: [ + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + internalType: 'uint256', + name: 'addedValue', + type: 'uint256', + }, + ], + name: 'increaseAllowance', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'name', + outputs: [ + { + internalType: 'string', + name: '', + type: 'string', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + ], + name: 'nonces', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'numCheckpoints', + outputs: [ + { + internalType: 'uint32', + name: '', + type: 'uint32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'owner', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256', + }, + { + internalType: 'uint8', + name: 'v', + type: 'uint8', + }, + { + internalType: 'bytes32', + name: 'r', + type: 'bytes32', + }, + { + internalType: 'bytes32', + name: 's', + type: 'bytes32', + }, + ], + name: 'permit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'renounceOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes', + name: 'initializeParams', + type: 'bytes', + }, + ], + name: 'setUp', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes4', + name: 'interfaceId', + type: 'bytes4', + }, + ], + name: 'supportsInterface', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'symbol', + outputs: [ + { + internalType: 'string', + name: '', + type: 'string', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalSupply', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'snapshotId', + type: 'uint256', + }, + ], + name: 'totalSupplyAt', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'transfer', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'from', + type: 'address', + }, + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'transferFrom', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'transferOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'underlying', + outputs: [ + { + internalType: 'contract IERC20Upgradeable', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'withdrawTo', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const; + +export default VotesERC20Wrapper; diff --git a/src/components/ui/modals/DelegateModal.tsx b/src/components/ui/modals/DelegateModal.tsx index bcf804805e..1dd792877d 100644 --- a/src/components/ui/modals/DelegateModal.tsx +++ b/src/components/ui/modals/DelegateModal.tsx @@ -44,7 +44,7 @@ export function DelegateModal({ close }: { close: Function }) { validAddress = getAddress(await signer.resolveName(values.address)); } const votingTokenContract = - baseContracts.votesERC20WrapperMasterCopyContract.asSigner.attach(votesTokenContractAddress); + baseContracts.votesTokenMasterCopyContract.asSigner.attach(votesTokenContractAddress); delegateVote({ delegatee: validAddress, votingTokenContract, diff --git a/src/components/ui/modals/UnwrapToken.tsx b/src/components/ui/modals/UnwrapToken.tsx index b25974e8c1..8f63305d3b 100644 --- a/src/components/ui/modals/UnwrapToken.tsx +++ b/src/components/ui/modals/UnwrapToken.tsx @@ -1,18 +1,18 @@ import { Button, Flex, Input } from '@chakra-ui/react'; import { LabelWrapper } from '@decent-org/fractal-ui'; -import { VotesERC20Wrapper } from '@fractal-framework/fractal-contracts'; import { Formik, FormikProps } from 'formik'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { useAccount } from 'wagmi'; +import { getAddress, getContract } from 'viem'; +import { useAccount, useWalletClient } from 'wagmi'; import * as Yup from 'yup'; +import VotesERC20WrapperAbi from '../../../assets/abi/VotesERC20Wrapper'; import { useERC20LinearToken } from '../../../hooks/DAO/loaders/governance/useERC20LinearToken'; import useSafeContracts from '../../../hooks/safe/useSafeContracts'; import useApproval from '../../../hooks/utils/useApproval'; import { useFormHelpers } from '../../../hooks/utils/useFormHelpers'; import { useTransaction } from '../../../hooks/utils/useTransaction'; import { useFractal } from '../../../providers/App/AppProvider'; -import { useEthersSigner } from '../../../providers/Ethers/hooks/useEthersSigner'; import { AzoriusGovernance, BigIntValuePair } from '../../../types'; import { formatCoin } from '../../../utils'; import { BigIntInput } from '../forms/BigIntInput'; @@ -20,12 +20,11 @@ import { BigIntInput } from '../forms/BigIntInput'; export function UnwrapToken({ close }: { close: () => void }) { const { governance, governanceContracts } = useFractal(); const azoriusGovernance = governance as AzoriusGovernance; - const signer = useEthersSigner(); const { address: account } = useAccount(); const baseContracts = useSafeContracts(); const { loadERC20TokenAccountData } = useERC20LinearToken({ onMount: false }); - const [contractCall, pending] = useTransaction(); + const [, pending, contractCallViem] = useTransaction(); const { approved, approveTransaction, @@ -40,17 +39,21 @@ export function UnwrapToken({ close }: { close: () => void }) { const { t } = useTranslation(['modals', 'treasury']); const { restrictChars } = useFormHelpers(); + const { data: walletClient } = useWalletClient(); + const handleFormSubmit = useCallback( (amount: BigIntValuePair) => { const { votesTokenContractAddress } = governanceContracts; - if (!votesTokenContractAddress || !signer || !account) return; - const votesTokenContract = - baseContracts?.votesERC20WrapperMasterCopyContract?.asSigner.attach( - votesTokenContractAddress, - ); - const wrapperTokenContract = votesTokenContract as VotesERC20Wrapper; - contractCall({ - contractFn: () => wrapperTokenContract.withdrawTo(account, amount.bigintValue!), + if (!votesTokenContractAddress || !account || !walletClient) return; + + const wrapperTokenContract = getContract({ + abi: VotesERC20WrapperAbi, + address: getAddress(votesTokenContractAddress), + client: walletClient, + }); + + contractCallViem({ + contractFn: () => wrapperTokenContract.write.withdrawTo([account, amount.bigintValue!]), pendingMessage: t('unwrapTokenPendingMessage'), failedMessage: t('unwrapTokenFailedMessage'), successMessage: t('unwrapTokenSuccessMessage'), @@ -64,13 +67,12 @@ export function UnwrapToken({ close }: { close: () => void }) { }, [ account, - contractCall, + contractCallViem, governanceContracts, - signer, + walletClient, close, t, loadERC20TokenAccountData, - baseContracts, ], ); diff --git a/src/components/ui/modals/WrapToken.tsx b/src/components/ui/modals/WrapToken.tsx index 9abdf840ad..ccfec436d7 100644 --- a/src/components/ui/modals/WrapToken.tsx +++ b/src/components/ui/modals/WrapToken.tsx @@ -3,9 +3,10 @@ import { LabelWrapper } from '@decent-org/fractal-ui'; import { Formik, FormikProps } from 'formik'; import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { erc20Abi, getContract } from 'viem'; +import { erc20Abi, getAddress, getContract } from 'viem'; import { useAccount, usePublicClient, useWalletClient } from 'wagmi'; import * as Yup from 'yup'; +import VotesERC20WrapperAbi from '../../../assets/abi/VotesERC20Wrapper'; import { logError } from '../../../helpers/errorLogging'; import { useERC20LinearToken } from '../../../hooks/DAO/loaders/governance/useERC20LinearToken'; import useSafeContracts from '../../../hooks/safe/useSafeContracts'; @@ -32,7 +33,7 @@ export function WrapToken({ close }: { close: () => void }) { const baseContracts = useSafeContracts(); const { loadERC20TokenAccountData } = useERC20LinearToken({ onMount: false }); - const [contractCall, pending] = useTransaction(); + const [, pending, contractCallViem] = useTransaction(); const { approved, approveTransaction, @@ -59,7 +60,7 @@ export function WrapToken({ close }: { close: () => void }) { const baseTokenContract = getContract({ address: azoriusGovernance.votesToken.underlyingTokenData.address, abi: erc20Abi, - client: { wallet: walletClient, public: publicClient }, + client: publicClient, }); try { const [balance, decimals] = await Promise.all([ @@ -79,7 +80,7 @@ export function WrapToken({ close }: { close: () => void }) { logError(e); return; } - }, [account, azoriusGovernance.votesToken, publicClient, walletClient]); + }, [account, azoriusGovernance.votesToken, publicClient]); useEffect(() => { getUserUnderlyingTokenBalance(); @@ -88,13 +89,17 @@ export function WrapToken({ close }: { close: () => void }) { const handleFormSubmit = useCallback( (amount: BigIntValuePair) => { const { votesTokenContractAddress } = governanceContracts; - if (!votesTokenContractAddress || !signer || !account || !baseContracts) return; - const wrapperTokenContract = - baseContracts.votesERC20WrapperMasterCopyContract.asSigner.attach( - votesTokenContractAddress, - ); - contractCall({ - contractFn: () => wrapperTokenContract.depositFor(account, amount.bigintValue!), + if (!votesTokenContractAddress || !signer || !account || !baseContracts || !walletClient) + return; + + const wrapperTokenContract = getContract({ + abi: VotesERC20WrapperAbi, + address: getAddress(votesTokenContractAddress), + client: walletClient, + }); + + contractCallViem({ + contractFn: () => wrapperTokenContract.write.depositFor([account, amount.bigintValue!]), pendingMessage: t('wrapTokenPendingMessage'), failedMessage: t('wrapTokenFailedMessage'), successMessage: t('wrapTokenSuccessMessage'), @@ -108,13 +113,14 @@ export function WrapToken({ close }: { close: () => void }) { }, [ account, - contractCall, + contractCallViem, governanceContracts, signer, close, t, loadERC20TokenAccountData, baseContracts, + walletClient, ], ); diff --git a/src/hooks/DAO/loaders/useGovernanceContracts.ts b/src/hooks/DAO/loaders/useGovernanceContracts.ts index cc5b67c6e8..378f8e6272 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 { Address, getContract, getAddress } from 'viem'; import { usePublicClient } from 'wagmi'; +import VotesERC20WrapperAbi from '../../../assets/abi/VotesERC20Wrapper'; import { LockRelease__factory } from '../../../assets/typechain-types/dcnt'; import { useFractal } from '../../../providers/App/AppProvider'; import { GovernanceContractAction } from '../../../providers/App/governanceContracts/action'; @@ -23,11 +24,7 @@ export const useGovernanceContracts = () => { if (!baseContracts || !publicClient) { return; } - const { - fractalAzoriusMasterCopyContract, - votesERC20WrapperMasterCopyContract, - linearVotingMasterCopyContract, - } = baseContracts; + const { fractalAzoriusMasterCopyContract, linearVotingMasterCopyContract } = baseContracts; const azoriusModule = getAzoriusModuleFromModules(fractalModules); const azoriusModuleContract = azoriusModule?.moduleContract as Azorius; @@ -62,9 +59,14 @@ export const useGovernanceContracts = () => { ozLinearVotingContractAddress, ); govTokenAddress = await ozLinearVotingContract.governanceToken(); - const possibleERC20Wrapper = - votesERC20WrapperMasterCopyContract.asProvider.attach(govTokenAddress); - underlyingTokenAddress = await possibleERC20Wrapper.underlying().catch(() => { + + const possibleERC20Wrapper = getContract({ + abi: VotesERC20WrapperAbi, + address: getAddress(govTokenAddress), + client: publicClient, + }); + + underlyingTokenAddress = await possibleERC20Wrapper.read.underlying().catch(() => { // if the underlying token is not an ERC20Wrapper, this will throw an error, // so we catch it and return undefined return undefined; diff --git a/src/hooks/DAO/useBuildDAOTx.ts b/src/hooks/DAO/useBuildDAOTx.ts index 24af6c17c4..6955f50bef 100644 --- a/src/hooks/DAO/useBuildDAOTx.ts +++ b/src/hooks/DAO/useBuildDAOTx.ts @@ -18,7 +18,7 @@ const useBuildDAOTx = () => { const signerOrProvider = useSignerOrProvider(); const { createOptions, - contracts: { fallbackHandler }, + contracts: { fallbackHandler, votesERC20WrapperMasterCopy }, } = useNetworkConfig(); const { @@ -34,7 +34,7 @@ const useBuildDAOTx = () => { parentAddress?: string, parentTokenAddress?: string, ) => { - let azoriusContracts; + let azoriusContracts: AzoriusContracts | undefined; if (!user.address || !signerOrProvider || !baseContracts) { return; @@ -56,7 +56,6 @@ const useBuildDAOTx = () => { freezeERC721VotingMasterCopyContract, votesTokenMasterCopyContract, claimingMasterCopyContract, - votesERC20WrapperMasterCopyContract, keyValuePairsContract, } = baseContracts; @@ -87,8 +86,7 @@ const useBuildDAOTx = () => { azoriusFreezeGuardMasterCopyContract: azoriusFreezeGuardMasterCopyContract.asSigner, votesTokenMasterCopyContract: votesTokenMasterCopyContract.asSigner, claimingMasterCopyContract: claimingMasterCopyContract.asSigner, - votesERC20WrapperMasterCopyContract: votesERC20WrapperMasterCopyContract.asSigner, - } as AzoriusContracts; + }; } const buildrerBaseContracts = { @@ -111,6 +109,7 @@ const useBuildDAOTx = () => { azoriusContracts, daoData, fallbackHandler, + votesERC20WrapperMasterCopy, parentAddress, parentTokenAddress, ); @@ -157,6 +156,7 @@ const useBuildDAOTx = () => { governance, createOptions, fallbackHandler, + votesERC20WrapperMasterCopy, ], ); diff --git a/src/hooks/DAO/useDeployAzorius.ts b/src/hooks/DAO/useDeployAzorius.ts index d509ffb847..c6dcf46905 100644 --- a/src/hooks/DAO/useDeployAzorius.ts +++ b/src/hooks/DAO/useDeployAzorius.ts @@ -21,7 +21,7 @@ const useDeployAzorius = () => { const signerOrProvider = useSignerOrProvider(); const navigate = useNavigate(); const { - contracts: { fallbackHandler }, + contracts: { fallbackHandler, votesERC20WrapperMasterCopy }, addressPrefix, } = useNetworkConfig(); const { @@ -56,7 +56,6 @@ const useDeployAzorius = () => { freezeERC20VotingMasterCopyContract, votesTokenMasterCopyContract, claimingMasterCopyContract, - votesERC20WrapperMasterCopyContract, keyValuePairsContract, } = baseContracts; let azoriusContracts; @@ -67,7 +66,6 @@ const useDeployAzorius = () => { azoriusFreezeGuardMasterCopyContract: azoriusFreezeGuardMasterCopyContract.asProvider, votesTokenMasterCopyContract: votesTokenMasterCopyContract.asProvider, claimingMasterCopyContract: claimingMasterCopyContract.asProvider, - votesERC20WrapperMasterCopyContract: votesERC20WrapperMasterCopyContract.asProvider, } as AzoriusContracts; const builderBaseContracts = { @@ -89,6 +87,7 @@ const useDeployAzorius = () => { azoriusContracts, daoData, fallbackHandler, + votesERC20WrapperMasterCopy, undefined, undefined, ); @@ -146,6 +145,7 @@ const useDeployAzorius = () => { safe, fallbackHandler, addressPrefix, + votesERC20WrapperMasterCopy, ], ); diff --git a/src/hooks/safe/useSafeContracts.ts b/src/hooks/safe/useSafeContracts.ts index 2ad2d8ee4a..9860758be7 100644 --- a/src/hooks/safe/useSafeContracts.ts +++ b/src/hooks/safe/useSafeContracts.ts @@ -11,7 +11,6 @@ import { LinearERC20Voting__factory, Azorius__factory, ERC20Claim__factory, - VotesERC20Wrapper__factory, KeyValuePairs__factory, LinearERC721Voting__factory, ERC721FreezeVoting__factory, @@ -44,7 +43,6 @@ export default function useSafeContracts() { erc721FreezeVotingMasterCopy, votesERC20MasterCopy, claimingMasterCopy, - votesERC20WrapperMasterCopy, linearVotingERC721MasterCopy, keyValuePairs, }, @@ -139,11 +137,6 @@ export default function useSafeContracts() { asProvider: ERC20Claim__factory.connect(claimingMasterCopy, provider), }; - const votesERC20WrapperMasterCopyContract = { - asSigner: VotesERC20Wrapper__factory.connect(votesERC20WrapperMasterCopy, signerOrProvider), - asProvider: VotesERC20Wrapper__factory.connect(votesERC20WrapperMasterCopy, provider), - }; - const keyValuePairsContract = { asSigner: KeyValuePairs__factory.connect(keyValuePairs, signerOrProvider), asProvider: KeyValuePairs__factory.connect(keyValuePairs, provider), @@ -165,7 +158,6 @@ export default function useSafeContracts() { freezeERC721VotingMasterCopyContract, votesTokenMasterCopyContract, claimingMasterCopyContract, - votesERC20WrapperMasterCopyContract, linearVotingERC721MasterCopyContract, keyValuePairsContract, }; @@ -184,7 +176,6 @@ export default function useSafeContracts() { erc20FreezeVotingMasterCopy, votesERC20MasterCopy, claimingMasterCopy, - votesERC20WrapperMasterCopy, linearVotingERC721MasterCopy, erc721FreezeVotingMasterCopy, keyValuePairs, diff --git a/src/hooks/utils/useApproval.tsx b/src/hooks/utils/useApproval.tsx index e9dd48270b..923a1bd59b 100644 --- a/src/hooks/utils/useApproval.tsx +++ b/src/hooks/utils/useApproval.tsx @@ -1,15 +1,11 @@ -import { VotesERC20, VotesERC20Wrapper } from '@fractal-framework/fractal-contracts'; +import { VotesERC20 } from '@fractal-framework/fractal-contracts'; import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { maxUint256 } from 'viem'; import { useAccount } from 'wagmi'; import { useTransaction } from './useTransaction'; -const useApproval = ( - tokenContract?: VotesERC20 | VotesERC20Wrapper, - spenderAddress?: string, - userBalance?: bigint, -) => { +const useApproval = (tokenContract?: VotesERC20, spenderAddress?: string, userBalance?: bigint) => { const { address: account } = useAccount(); const [allowance, setAllowance] = useState(0n); const [approved, setApproved] = useState(false); diff --git a/src/hooks/utils/useTransaction.ts b/src/hooks/utils/useTransaction.ts index 1e0cb1c4ed..db41b3da0d 100644 --- a/src/hooks/utils/useTransaction.ts +++ b/src/hooks/utils/useTransaction.ts @@ -2,6 +2,8 @@ import { ContractReceipt, ethers } from 'ethers'; import React, { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { toast } from 'react-toastify'; +import { Hash, TransactionReceipt } from 'viem'; +import { usePublicClient } from 'wagmi'; import { logError } from '../../helpers/errorLogging'; interface ProviderRpcError extends Error { @@ -10,6 +12,16 @@ interface ProviderRpcError extends Error { data?: any; } +interface ContractCallParamsViem { + contractFn: () => Promise; + pendingMessage: string; + failedMessage: string; + successMessage: string; + failedCallback?: () => void; + successCallback?: (txReceipt: TransactionReceipt) => void; + completedCallback?: () => void; +} + interface ContractCallParams { contractFn: () => Promise; pendingMessage: string; @@ -23,6 +35,8 @@ interface ContractCallParams { const useTransaction = () => { const [pending, setPending] = useState(false); const { t } = useTranslation(['transaction', 'common']); + const publicClient = usePublicClient(); + const contractCall = useCallback( (params: ContractCallParams) => { let toastId: React.ReactText; @@ -36,7 +50,7 @@ const useTransaction = () => { setPending(true); params .contractFn() - .then((txResponse: ethers.ContractTransaction) => { + .then(txResponse => { return Promise.all([txResponse.wait(), toastId]); }) .then(([txReceipt, toastID]) => { @@ -76,7 +90,65 @@ const useTransaction = () => { [t], ); - return [contractCall, pending] as const; + const contractCallViem = useCallback( + (params: ContractCallParamsViem) => { + if (!publicClient) return; + + let toastId: React.ReactText; + toastId = toast(params.pendingMessage, { + autoClose: false, + closeOnClick: false, + draggable: false, + closeButton: false, + progress: 1, + }); + setPending(true); + params + .contractFn() + .then(txReceipt => { + return Promise.all([ + publicClient.waitForTransactionReceipt({ hash: txReceipt }), + toastId, + ]); + }) + .then(([txReceipt, toastID]) => { + toast.dismiss(toastID); + if (txReceipt.status === 'reverted') { + toast.error(params.failedMessage); + if (params.failedCallback) params.failedCallback(); + } else if (txReceipt.status === 'success') { + toast(params.successMessage); + if (params.successCallback) params.successCallback(txReceipt); + } else { + toast.error(t('errorTransactionUnknown')); + if (params.failedCallback) params.failedCallback(); + } + if (params.completedCallback) params.completedCallback(); + + // Give the block event emitter a couple seconds to play the latest + // block on the app state, before informing app that the transaction + // is completed. + setTimeout(() => { + setPending(false); + }, 2000); + }) + .catch((error: ProviderRpcError) => { + logError(error); + toast.dismiss(toastId); + setPending(false); + + if (error.code === 4001) { + toast.error(t('errorUserDeniedTransaction')); + return; + } + + toast.error(t('errorGeneral', { ns: 'common' })); + }); + }, + [t, publicClient], + ); + + return [contractCall, pending, contractCallViem] as const; }; export { useTransaction }; diff --git a/src/models/AzoriusTxBuilder.ts b/src/models/AzoriusTxBuilder.ts index e1e6189d38..ca4a7658b3 100644 --- a/src/models/AzoriusTxBuilder.ts +++ b/src/models/AzoriusTxBuilder.ts @@ -11,7 +11,7 @@ import { import { getCreate2Address, Address, - Hash, + Hex, encodePacked, keccak256, encodeAbiParameters, @@ -19,7 +19,9 @@ import { getAddress, isAddress, isHex, + encodeFunctionData, } from 'viem'; +import VotesERC20WrapperAbi from '../assets/abi/VotesERC20Wrapper'; import { GnosisSafeL2 } from '../assets/typechain-types/usul/@gnosis.pm/safe-contracts/contracts'; import { buildContractCall, getRandomBytes } from '../helpers'; import { @@ -37,11 +39,11 @@ import { generateContractByteCodeLinear, generateSalt } from './helpers/utils'; export class AzoriusTxBuilder extends BaseTxBuilder { private readonly safeContract: GnosisSafeL2; - private encodedSetupTokenData: Address | undefined; - private encodedSetupERC20WrapperData: Hash | undefined; - private encodedStrategySetupData: Hash | undefined; - private encodedSetupAzoriusData: Hash | undefined; - private encodedSetupTokenClaimData: Hash | undefined; + private encodedSetupTokenData: Hex | undefined; + private encodedSetupERC20WrapperData: Hex | undefined; + private encodedStrategySetupData: Hex | undefined; + private encodedSetupAzoriusData: Hex | undefined; + private encodedSetupTokenClaimData: Hex | undefined; private predictedTokenAddress: Address | undefined; private predictedStrategyAddress: Address | undefined; @@ -53,6 +55,8 @@ export class AzoriusTxBuilder extends BaseTxBuilder { public linearERC721VotingContract: LinearERC721Voting | undefined; public votesTokenContract: VotesERC20 | undefined; + private votesERC20WrapperMasterCopyAddress: string; + private tokenNonce: bigint; private strategyNonce: bigint; private azoriusNonce: bigint; @@ -64,6 +68,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { azoriusContracts: AzoriusContracts, daoData: AzoriusERC20DAO | AzoriusERC721DAO, safeContract: GnosisSafeL2, + votesERC20WrapperMasterCopyAddress: string, parentAddress?: Address, parentTokenAddress?: Address, ) { @@ -83,6 +88,8 @@ export class AzoriusTxBuilder extends BaseTxBuilder { this.strategyNonce = getRandomBytes(); this.azoriusNonce = getRandomBytes(); + this.votesERC20WrapperMasterCopyAddress = votesERC20WrapperMasterCopyAddress; + if (daoData.votingStrategyType === VotingStrategyType.LINEAR_ERC20) { daoData = daoData as AzoriusERC20DAO; if (!daoData.isTokenImported) { @@ -251,11 +258,7 @@ export class AzoriusTxBuilder extends BaseTxBuilder { return buildContractCall( this.baseContracts.zodiacModuleProxyFactoryContract, 'deployModule', - [ - this.azoriusContracts!.votesERC20WrapperMasterCopyContract.address, - this.encodedSetupERC20WrapperData, - this.tokenNonce, - ], + [this.votesERC20WrapperMasterCopyAddress, this.encodedSetupERC20WrapperData, this.tokenNonce], 0, false, ); @@ -274,20 +277,16 @@ export class AzoriusTxBuilder extends BaseTxBuilder { tokenImportAddress, ]); - const encodedSetupERC20WrapperData = - this.azoriusContracts!.votesERC20WrapperMasterCopyContract.interface.encodeFunctionData( - 'setUp', - [encodedInitTokenData], - ); - if (!isHex(encodedSetupERC20WrapperData)) { - throw new Error('Error encoding setup ERC-20 Wrapper data - interface encoding failed'); - } - this.encodedSetupERC20WrapperData = encodedSetupERC20WrapperData; + this.encodedSetupERC20WrapperData = encodeFunctionData({ + abi: VotesERC20WrapperAbi, + functionName: 'setUp', + args: [encodedInitTokenData], + }); } public setPredictedERC20WrapperAddress() { const tokenByteCodeLinear = generateContractByteCodeLinear( - getAddress(this.azoriusContracts!.votesERC20WrapperMasterCopyContract.address), + getAddress(this.votesERC20WrapperMasterCopyAddress), ); const tokenSalt = generateSalt(this.encodedSetupERC20WrapperData!, this.tokenNonce); diff --git a/src/models/TxBuilderFactory.ts b/src/models/TxBuilderFactory.ts index 81682876f5..a45654eea2 100644 --- a/src/models/TxBuilderFactory.ts +++ b/src/models/TxBuilderFactory.ts @@ -29,12 +29,15 @@ export class TxBuilderFactory extends BaseTxBuilder { private safeContract: GnosisSafeL2 | undefined; public fallbackHandler: string; + private votesERC20WrapperMasterCopyAddress: string; + constructor( signerOrProvider: ethers.Signer | any, baseContracts: BaseContracts, azoriusContracts: AzoriusContracts | undefined, daoData: SafeMultisigDAO | AzoriusERC20DAO | AzoriusERC721DAO | SubDAO, fallbackHandler: string, + votesERC20WrapperMasterCopyAddress: string, parentAddress?: string, parentTokenAddress?: string, ) { @@ -49,6 +52,7 @@ export class TxBuilderFactory extends BaseTxBuilder { this.fallbackHandler = fallbackHandler; this.saltNum = getRandomBytes(); + this.votesERC20WrapperMasterCopyAddress = votesERC20WrapperMasterCopyAddress; } public setSafeContract(safeAddress: string) { @@ -130,6 +134,7 @@ export class TxBuilderFactory extends BaseTxBuilder { this.azoriusContracts!, this.daoData as AzoriusERC20DAO, this.safeContract!, + this.votesERC20WrapperMasterCopyAddress, this.parentAddress ? getAddress(this.parentAddress) : undefined, this.parentTokenAddress ? getAddress(this.parentTokenAddress) : undefined, ); diff --git a/src/models/helpers/safeData.ts b/src/models/helpers/safeData.ts index a601e90cf3..622c229eba 100644 --- a/src/models/helpers/safeData.ts +++ b/src/models/helpers/safeData.ts @@ -10,7 +10,7 @@ import { isHex, hexToBigInt, } from 'viem'; -import GnosisSafeL2ABI from '../../assets/abi/GnosisSafeL2'; +import GnosisSafeL2Abi from '../../assets/abi/GnosisSafeL2'; import { MultiSend } from '../../assets/typechain-types/usul'; import { GnosisSafeL2 } from '../../assets/typechain-types/usul/@gnosis.pm/safe-contracts/contracts'; import { buildContractCall } from '../../helpers/crypto'; @@ -32,16 +32,16 @@ export const safeData = async ( const createSafeCalldata = encodeFunctionData({ functionName: 'setup', args: [ - signers, - 1, // Threshold + signers.map(signer => getAddress(signer)), + 1n, // Threshold zeroAddress, zeroHash, - fallbackHandler, + getAddress(fallbackHandler), zeroAddress, - 0, + 0n, zeroAddress, ], - abi: GnosisSafeL2ABI, + abi: GnosisSafeL2Abi, }); const safeFactoryContractProxyCreationCode = await safeFactoryContract.proxyCreationCode(); diff --git a/src/providers/NetworkConfig/networks/base.ts b/src/providers/NetworkConfig/networks/base.ts index f90ecba350..86bb5b08f7 100644 --- a/src/providers/NetworkConfig/networks/base.ts +++ b/src/providers/NetworkConfig/networks/base.ts @@ -19,15 +19,18 @@ import { getSafeL2SingletonDeployment, getCompatibilityFallbackHandlerDeployment, } from '@safe-global/safe-deployments'; +import { getAddress } from 'viem'; import { base } from 'wagmi/chains'; import { GovernanceType } from '../../../types'; import { NetworkConfig } from '../../../types/network'; const SAFE_VERSION = '1.3.0'; +const chain = base; + export const baseConfig: NetworkConfig = { order: 10, - chain: base, + chain, rpcEndpoint: `https://base-mainnet.g.alchemy.com/v2/${import.meta.env.VITE_APP_ALCHEMY_BASE_API_KEY}`, safeBaseURL: 'https://safe-transaction-base.safe.global', etherscanBaseURL: 'https://basescan.org/', @@ -53,21 +56,21 @@ export const baseConfig: NetworkConfig = { multisigFreezeGuardMasterCopy: MultisigFreezeGuard.address, fallbackHandler: getCompatibilityFallbackHandlerDeployment({ version: SAFE_VERSION, - network: base.id.toString(), - })?.networkAddresses[base.id.toString()]!, - safe: getSafeL2SingletonDeployment({ version: SAFE_VERSION, network: base.id.toString() }) - ?.networkAddresses[base.id.toString()]!, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, + safe: getSafeL2SingletonDeployment({ version: SAFE_VERSION, network: chain.id.toString() }) + ?.networkAddresses[chain.id.toString()]!, safeFactory: getProxyFactoryDeployment({ version: SAFE_VERSION, - network: base.id.toString(), - })?.networkAddresses[base.id.toString()]!, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, zodiacModuleProxyFactory: ModuleProxyFactory.address, linearVotingMasterCopy: LinearERC20Voting.address, multisend: getMultiSendCallOnlyDeployment({ version: SAFE_VERSION, - network: base.id.toString(), - })?.networkAddresses[base.id.toString()]!, - votesERC20WrapperMasterCopy: VotesERC20Wrapper.address, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, + votesERC20WrapperMasterCopy: getAddress(VotesERC20Wrapper.address), keyValuePairs: KeyValuePairs.address, }, staking: {}, diff --git a/src/providers/NetworkConfig/networks/baseSepolia.ts b/src/providers/NetworkConfig/networks/baseSepolia.ts index 10ffc53e39..3b9c9c9fcd 100644 --- a/src/providers/NetworkConfig/networks/baseSepolia.ts +++ b/src/providers/NetworkConfig/networks/baseSepolia.ts @@ -19,15 +19,18 @@ import { getSafeL2SingletonDeployment, getCompatibilityFallbackHandlerDeployment, } from '@safe-global/safe-deployments'; +import { getAddress } from 'viem'; import { baseSepolia } from 'wagmi/chains'; import { GovernanceType } from '../../../types'; import { NetworkConfig } from '../../../types/network'; const SAFE_VERSION = '1.3.0'; +const chain = baseSepolia; + export const baseSepoliaConfig: NetworkConfig = { order: 40, - chain: baseSepolia, + chain, rpcEndpoint: `https://base-sepolia.g.alchemy.com/v2/${import.meta.env.VITE_APP_ALCHEMY_BASE_SEPOLIA_API_KEY}`, safeBaseURL: 'https://safe-transaction-base-sepolia.safe.global', etherscanBaseURL: 'https://sepolia.basescan.org/', @@ -53,23 +56,23 @@ export const baseSepoliaConfig: NetworkConfig = { multisigFreezeGuardMasterCopy: MultisigFreezeGuard.address, fallbackHandler: getCompatibilityFallbackHandlerDeployment({ version: SAFE_VERSION, - network: baseSepolia.id.toString(), - })?.networkAddresses[baseSepolia.id.toString()]!, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, safe: getSafeL2SingletonDeployment({ version: SAFE_VERSION, - network: baseSepolia.id.toString(), - })?.networkAddresses[baseSepolia.id.toString()]!, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, safeFactory: getProxyFactoryDeployment({ version: SAFE_VERSION, - network: baseSepolia.id.toString(), - })?.networkAddresses[baseSepolia.id.toString()]!, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, zodiacModuleProxyFactory: ModuleProxyFactory.address, linearVotingMasterCopy: LinearERC20Voting.address, multisend: getMultiSendCallOnlyDeployment({ version: SAFE_VERSION, - network: baseSepolia.id.toString(), - })?.networkAddresses[baseSepolia.id.toString()]!, - votesERC20WrapperMasterCopy: VotesERC20Wrapper.address, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, + votesERC20WrapperMasterCopy: getAddress(VotesERC20Wrapper.address), keyValuePairs: KeyValuePairs.address, }, staking: {}, diff --git a/src/providers/NetworkConfig/networks/mainnet.ts b/src/providers/NetworkConfig/networks/mainnet.ts index 76b2301703..785bef1a12 100644 --- a/src/providers/NetworkConfig/networks/mainnet.ts +++ b/src/providers/NetworkConfig/networks/mainnet.ts @@ -19,15 +19,18 @@ import { getSafeL2SingletonDeployment, getCompatibilityFallbackHandlerDeployment, } from '@safe-global/safe-deployments'; +import { getAddress } from 'viem'; import { mainnet } from 'wagmi/chains'; import { GovernanceType } from '../../../types'; import { NetworkConfig } from '../../../types/network'; const SAFE_VERSION = '1.3.0'; +const chain = mainnet; + export const mainnetConfig: NetworkConfig = { order: 0, - chain: mainnet, + chain, rpcEndpoint: `https://eth-mainnet.g.alchemy.com/v2/${import.meta.env.VITE_APP_ALCHEMY_MAINNET_API_KEY}`, safeBaseURL: 'https://safe-transaction-mainnet.safe.global', etherscanBaseURL: 'https://etherscan.io', @@ -53,21 +56,21 @@ export const mainnetConfig: NetworkConfig = { multisigFreezeGuardMasterCopy: MultisigFreezeGuard.address, fallbackHandler: getCompatibilityFallbackHandlerDeployment({ version: SAFE_VERSION, - network: mainnet.id.toString(), - })?.networkAddresses[mainnet.id.toString()]!, - safe: getSafeL2SingletonDeployment({ version: SAFE_VERSION, network: mainnet.id.toString() }) - ?.networkAddresses[mainnet.id.toString()]!, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, + safe: getSafeL2SingletonDeployment({ version: SAFE_VERSION, network: chain.id.toString() }) + ?.networkAddresses[chain.id.toString()]!, safeFactory: getProxyFactoryDeployment({ version: SAFE_VERSION, - network: mainnet.id.toString(), - })?.networkAddresses[mainnet.id.toString()]!, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, zodiacModuleProxyFactory: ModuleProxyFactory.address, linearVotingMasterCopy: LinearERC20Voting.address, multisend: getMultiSendCallOnlyDeployment({ version: SAFE_VERSION, - network: mainnet.id.toString(), - })?.networkAddresses[mainnet.id.toString()]!, - votesERC20WrapperMasterCopy: VotesERC20Wrapper.address, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, + votesERC20WrapperMasterCopy: getAddress(VotesERC20Wrapper.address), keyValuePairs: KeyValuePairs.address, }, staking: { diff --git a/src/providers/NetworkConfig/networks/optimism.ts b/src/providers/NetworkConfig/networks/optimism.ts index f3da6f13c1..7ebfea0c9b 100644 --- a/src/providers/NetworkConfig/networks/optimism.ts +++ b/src/providers/NetworkConfig/networks/optimism.ts @@ -19,15 +19,18 @@ import { getSafeL2SingletonDeployment, getCompatibilityFallbackHandlerDeployment, } from '@safe-global/safe-deployments'; +import { getAddress } from 'viem'; import { optimism } from 'wagmi/chains'; import { GovernanceType } from '../../../types'; import { NetworkConfig } from '../../../types/network'; const SAFE_VERSION = '1.3.0'; +const chain = optimism; + export const optimismConfig: NetworkConfig = { order: 15, - chain: optimism, + chain, rpcEndpoint: `https://opt-mainnet.g.alchemy.com/v2/${import.meta.env.VITE_APP_ALCHEMY_OPTIMISM_API_KEY}`, safeBaseURL: 'https://safe-transaction-optimism.safe.global', etherscanBaseURL: 'https://optimistic.etherscan.io/', @@ -53,21 +56,21 @@ export const optimismConfig: NetworkConfig = { multisigFreezeGuardMasterCopy: MultisigFreezeGuard.address, fallbackHandler: getCompatibilityFallbackHandlerDeployment({ version: SAFE_VERSION, - network: optimism.id.toString(), - })?.networkAddresses[optimism.id.toString()]!, - safe: getSafeL2SingletonDeployment({ version: SAFE_VERSION, network: optimism.id.toString() }) - ?.networkAddresses[optimism.id.toString()]!, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, + safe: getSafeL2SingletonDeployment({ version: SAFE_VERSION, network: chain.id.toString() }) + ?.networkAddresses[chain.id.toString()]!, safeFactory: getProxyFactoryDeployment({ version: SAFE_VERSION, - network: optimism.id.toString(), - })?.networkAddresses[optimism.id.toString()]!, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, zodiacModuleProxyFactory: ModuleProxyFactory.address, linearVotingMasterCopy: LinearERC20Voting.address, multisend: getMultiSendCallOnlyDeployment({ version: SAFE_VERSION, - network: optimism.id.toString(), - })?.networkAddresses[optimism.id.toString()]!, - votesERC20WrapperMasterCopy: VotesERC20Wrapper.address, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, + votesERC20WrapperMasterCopy: getAddress(VotesERC20Wrapper.address), keyValuePairs: KeyValuePairs.address, }, staking: {}, diff --git a/src/providers/NetworkConfig/networks/polygon.ts b/src/providers/NetworkConfig/networks/polygon.ts index 4f6507c315..272d547a6c 100644 --- a/src/providers/NetworkConfig/networks/polygon.ts +++ b/src/providers/NetworkConfig/networks/polygon.ts @@ -19,15 +19,18 @@ import { getSafeL2SingletonDeployment, getCompatibilityFallbackHandlerDeployment, } from '@safe-global/safe-deployments'; +import { getAddress } from 'viem'; import { polygon } from 'wagmi/chains'; import { GovernanceType } from '../../../types'; import { NetworkConfig } from '../../../types/network'; const SAFE_VERSION = '1.3.0'; +const chain = polygon; + export const polygonConfig: NetworkConfig = { order: 20, - chain: polygon, + chain, rpcEndpoint: `https://polygon-mainnet.g.alchemy.com/v2/${import.meta.env.VITE_APP_ALCHEMY_POLYGON_API_KEY}`, safeBaseURL: 'https://safe-transaction-polygon.safe.global', etherscanBaseURL: 'https://polygonscan.com', @@ -53,21 +56,21 @@ export const polygonConfig: NetworkConfig = { multisigFreezeGuardMasterCopy: MultisigFreezeGuard.address, fallbackHandler: getCompatibilityFallbackHandlerDeployment({ version: SAFE_VERSION, - network: polygon.id.toString(), - })?.networkAddresses[polygon.id.toString()]!, - safe: getSafeL2SingletonDeployment({ version: SAFE_VERSION, network: polygon.id.toString() }) - ?.networkAddresses[polygon.id.toString()]!, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, + safe: getSafeL2SingletonDeployment({ version: SAFE_VERSION, network: chain.id.toString() }) + ?.networkAddresses[chain.id.toString()]!, safeFactory: getProxyFactoryDeployment({ version: SAFE_VERSION, - network: polygon.id.toString(), - })?.networkAddresses[polygon.id.toString()]!, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, zodiacModuleProxyFactory: ModuleProxyFactory.address, linearVotingMasterCopy: LinearERC20Voting.address, multisend: getMultiSendCallOnlyDeployment({ version: SAFE_VERSION, - network: polygon.id.toString(), - })?.networkAddresses[polygon.id.toString()]!, - votesERC20WrapperMasterCopy: VotesERC20Wrapper.address, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, + votesERC20WrapperMasterCopy: getAddress(VotesERC20Wrapper.address), keyValuePairs: KeyValuePairs.address, }, staking: {}, diff --git a/src/providers/NetworkConfig/networks/sepolia.ts b/src/providers/NetworkConfig/networks/sepolia.ts index 62443ab27d..5095962b66 100644 --- a/src/providers/NetworkConfig/networks/sepolia.ts +++ b/src/providers/NetworkConfig/networks/sepolia.ts @@ -19,15 +19,18 @@ import { getSafeL2SingletonDeployment, getCompatibilityFallbackHandlerDeployment, } from '@safe-global/safe-deployments'; +import { getAddress } from 'viem'; import { sepolia } from 'wagmi/chains'; import { GovernanceType } from '../../../types'; import { NetworkConfig } from '../../../types/network'; const SAFE_VERSION = '1.3.0'; +const chain = sepolia; + export const sepoliaConfig: NetworkConfig = { order: 30, - chain: sepolia, + chain, rpcEndpoint: `https://eth-sepolia.g.alchemy.com/v2/${import.meta.env.VITE_APP_ALCHEMY_SEPOLIA_API_KEY}`, safeBaseURL: 'https://safe-transaction-sepolia.safe.global', etherscanBaseURL: 'https://sepolia.etherscan.io', @@ -53,21 +56,21 @@ export const sepoliaConfig: NetworkConfig = { multisigFreezeGuardMasterCopy: MultisigFreezeGuard.address, fallbackHandler: getCompatibilityFallbackHandlerDeployment({ version: SAFE_VERSION, - network: sepolia.id.toString(), - })?.networkAddresses[sepolia.id.toString()]!, - safe: getSafeL2SingletonDeployment({ version: SAFE_VERSION, network: sepolia.id.toString() }) - ?.networkAddresses[sepolia.id.toString()]!, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, + safe: getSafeL2SingletonDeployment({ version: SAFE_VERSION, network: chain.id.toString() }) + ?.networkAddresses[chain.id.toString()]!, safeFactory: getProxyFactoryDeployment({ version: SAFE_VERSION, - network: sepolia.id.toString(), - })?.networkAddresses[sepolia.id.toString()]!, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, zodiacModuleProxyFactory: ModuleProxyFactory.address, linearVotingMasterCopy: LinearERC20Voting.address, multisend: getMultiSendCallOnlyDeployment({ version: SAFE_VERSION, - network: sepolia.id.toString(), - })?.networkAddresses[sepolia.id.toString()]!, - votesERC20WrapperMasterCopy: VotesERC20Wrapper.address, + network: chain.id.toString(), + })?.networkAddresses[chain.id.toString()]!, + votesERC20WrapperMasterCopy: getAddress(VotesERC20Wrapper.address), keyValuePairs: KeyValuePairs.address, }, staking: {}, diff --git a/src/types/fractal.ts b/src/types/fractal.ts index 86770342cb..4b71576af9 100644 --- a/src/types/fractal.ts +++ b/src/types/fractal.ts @@ -11,7 +11,6 @@ import { MultisigFreezeVoting, VotesERC20, MultisigFreezeGuard, - VotesERC20Wrapper, KeyValuePairs, ERC721FreezeVoting, LinearERC721Voting, @@ -351,7 +350,6 @@ export interface FractalContracts { freezeERC721VotingMasterCopyContract: ContractConnection; votesTokenMasterCopyContract: ContractConnection; claimingMasterCopyContract: ContractConnection; - votesERC20WrapperMasterCopyContract: ContractConnection; keyValuePairsContract: ContractConnection; } diff --git a/src/types/network.ts b/src/types/network.ts index 2ad64d6a68..1fad136fff 100644 --- a/src/types/network.ts +++ b/src/types/network.ts @@ -1,5 +1,5 @@ import { ethers } from 'ethers'; -import { Chain } from 'viem'; +import { Address, Chain } from 'viem'; import { GovernanceType } from './fractal'; export type Providers = @@ -39,7 +39,7 @@ export type NetworkConfig = { multisigFreezeVotingMasterCopy: string; erc20FreezeVotingMasterCopy: string; erc721FreezeVotingMasterCopy: string; - votesERC20WrapperMasterCopy: string; + votesERC20WrapperMasterCopy: Address; keyValuePairs: string; }; staking: { diff --git a/src/types/strategyAzorius.ts b/src/types/strategyAzorius.ts index 8b3437db14..0e3388290e 100644 --- a/src/types/strategyAzorius.ts +++ b/src/types/strategyAzorius.ts @@ -4,7 +4,6 @@ import { AzoriusFreezeGuard, VotesERC20, ERC20Claim, - VotesERC20Wrapper, LinearERC721Voting, } from '@fractal-framework/fractal-contracts'; @@ -15,5 +14,4 @@ export interface AzoriusContracts { azoriusFreezeGuardMasterCopyContract: AzoriusFreezeGuard; votesTokenMasterCopyContract: VotesERC20; claimingMasterCopyContract: ERC20Claim; - votesERC20WrapperMasterCopyContract: VotesERC20Wrapper; }