diff --git a/src/components/Proposals/MultisigProposalDetails/TxActions.tsx b/src/components/Proposals/MultisigProposalDetails/TxActions.tsx index 263b9ee51d..64dd45e712 100644 --- a/src/components/Proposals/MultisigProposalDetails/TxActions.tsx +++ b/src/components/Proposals/MultisigProposalDetails/TxActions.tsx @@ -4,6 +4,7 @@ import { TypedDataSigner } from '@ethersproject/abstract-signer'; import { SafeMultisigTransactionWithTransfersResponse } from '@safe-global/safe-service-client'; import { Signer } from 'ethers'; import { useTranslation } from 'react-i18next'; +import { getAddress } from 'viem'; import { GnosisSafeL2__factory } from '../../../assets/typechain-types/usul/factories/@gnosis.pm/safe-contracts/contracts'; import { BACKGROUND_SEMI_TRANSPARENT } from '../../../constants/common'; import { buildSafeTransaction, buildSignatureBytes, EIP712_SAFE_TX_TYPE } from '../../../helpers'; @@ -49,6 +50,7 @@ export function TxActions({ proposal }: { proposal: MultisigProposal }) { try { const safeTx = buildSafeTransaction({ ...multisigTx, + to: getAddress(multisigTx.to), }); asyncRequest({ @@ -78,6 +80,7 @@ export function TxActions({ proposal }: { proposal: MultisigProposal }) { } const safeTx = buildSafeTransaction({ ...multisigTx, + to: getAddress(multisigTx.to), }); const signatures = buildSignatureBytes( multisigTx.confirmations.map(confirmation => ({ @@ -123,6 +126,7 @@ export function TxActions({ proposal }: { proposal: MultisigProposal }) { const safeContract = GnosisSafeL2__factory.connect(safe.address, signerOrProvider); const safeTx = buildSafeTransaction({ ...multisigTx, + to: getAddress(multisigTx.to), }); const signatures = buildSignatureBytes( multisigTx.confirmations.map(confirmation => ({ diff --git a/src/components/pages/DAOTreasury/hooks/useSendAssets.ts b/src/components/pages/DAOTreasury/hooks/useSendAssets.ts index bb1e76da91..ee8745c2ad 100644 --- a/src/components/pages/DAOTreasury/hooks/useSendAssets.ts +++ b/src/components/pages/DAOTreasury/hooks/useSendAssets.ts @@ -1,7 +1,7 @@ import { SafeBalanceUsdResponse } from '@safe-global/safe-service-client'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { encodeAbiParameters, parseAbiParameters, isAddress } from 'viem'; +import { encodeAbiParameters, parseAbiParameters, isAddress, getAddress, Hex } from 'viem'; import useSubmitProposal from '../../../../hooks/DAO/proposal/useSubmitProposal'; import { ProposalExecuteData } from '../../../../types'; import { formatCoin } from '../../../../utils/numberFormats'; @@ -30,33 +30,36 @@ const useSendAssets = ({ asset?.token?.symbol, ); - if (destinationAddress && isAddress(destinationAddress)) { - const calldatas = [ + let calldatas = ['0x' as Hex]; + let target = + isEth && destinationAddress ? getAddress(destinationAddress) : getAddress(asset.tokenAddress); + if (!isEth && destinationAddress && isAddress(destinationAddress)) { + calldatas = [ encodeAbiParameters(parseAbiParameters('address, uint256'), [ destinationAddress, transferAmount, ]), ]; + } - const proposalData: ProposalExecuteData = { - targets: [isEth ? destinationAddress : asset.tokenAddress], - values: [isEth ? transferAmount : 0n], - calldatas: isEth ? ['0x'] : calldatas, - metaData: { - title: t(isEth ? 'Send Eth' : 'Send Token', { ns: 'proposalMetadata' }), - description: description, - documentationUrl: '', - }, - }; + const proposalData: ProposalExecuteData = { + targets: [target], + values: [isEth ? transferAmount : 0n], + calldatas, + metaData: { + title: t(isEth ? 'Send Eth' : 'Send Token', { ns: 'proposalMetadata' }), + description: description, + documentationUrl: '', + }, + }; - await submitProposal({ - proposalData, - nonce, - pendingToastMessage: t('sendAssetsPendingToastMessage'), - successToastMessage: t('sendAssetsSuccessToastMessage'), - failedToastMessage: t('sendAssetsFailureToastMessage'), - }); - } + await submitProposal({ + proposalData, + nonce, + pendingToastMessage: t('sendAssetsPendingToastMessage'), + successToastMessage: t('sendAssetsSuccessToastMessage'), + failedToastMessage: t('sendAssetsFailureToastMessage'), + }); }, [ asset.tokenAddress, asset?.token?.decimals, diff --git a/src/components/pages/DaoSettings/components/Metadata/index.tsx b/src/components/pages/DaoSettings/components/Metadata/index.tsx index a73f123d3d..42d6470950 100644 --- a/src/components/pages/DaoSettings/components/Metadata/index.tsx +++ b/src/components/pages/DaoSettings/components/Metadata/index.tsx @@ -3,6 +3,7 @@ import { Flex, Text, Button, Divider } from '@chakra-ui/react'; import { useState, useEffect, ChangeEventHandler } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; +import { isHex, getAddress } from 'viem'; import { SettingsSection } from '..'; import { DAO_ROUTES } from '../../../../../constants/routes'; import useSubmitProposal from '../../../../../hooks/DAO/proposal/useSubmitProposal'; @@ -64,17 +65,22 @@ export default function MetadataContainer() { return; } const { fractalRegistryContract } = baseContracts; + const encodedUpdateDAOName = fractalRegistryContract.asProvider.interface.encodeFunctionData( + 'updateDAOName', + [name], + ); + if (!isHex(encodedUpdateDAOName)) { + return; + } const proposalData: ProposalExecuteData = { metaData: { title: t('Update Safe Name', { ns: 'proposalMetadata' }), description: '', documentationUrl: '', }, - targets: [fractalRegistryContract.asProvider.address], + targets: [getAddress(fractalRegistryContract.asProvider.address)], values: [0n], - calldatas: [ - fractalRegistryContract.asProvider.interface.encodeFunctionData('updateDAOName', [name]), - ], + calldatas: [encodedUpdateDAOName], }; submitProposal({ @@ -92,20 +98,22 @@ export default function MetadataContainer() { return; } const { keyValuePairsContract } = baseContracts; + const encodedUpdateValues = keyValuePairsContract.asProvider.interface.encodeFunctionData( + 'updateValues', + [['snapshotENS'], [snapshotENS]], + ); + if (!isHex(encodedUpdateValues)) { + return; + } const proposalData: ProposalExecuteData = { metaData: { title: t('Update Snapshot Space', { ns: 'proposalMetadata' }), description: '', documentationUrl: '', }, - targets: [keyValuePairsContract.asProvider.address], + targets: [getAddress(keyValuePairsContract.asProvider.address)], values: [0n], - calldatas: [ - keyValuePairsContract.asProvider.interface.encodeFunctionData('updateValues', [ - ['snapshotENS'], - [snapshotENS], - ]), - ], + calldatas: [encodedUpdateValues], }; submitProposal({ diff --git a/src/components/pages/DaoSettings/components/Signers/hooks/useAddSigner.ts b/src/components/pages/DaoSettings/components/Signers/hooks/useAddSigner.ts index 2a436f6a11..fe17b479de 100644 --- a/src/components/pages/DaoSettings/components/Signers/hooks/useAddSigner.ts +++ b/src/components/pages/DaoSettings/components/Signers/hooks/useAddSigner.ts @@ -1,5 +1,6 @@ import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; +import { getAddress, isHex } from 'viem'; import useSubmitProposal from '../../../../../../hooks/DAO/proposal/useSubmitProposal'; import { useFractal } from '../../../../../../providers/App/AppProvider'; import { ProposalExecuteData } from '../../../../../../types'; @@ -22,23 +23,25 @@ const useAddSigner = () => { daoAddress: string | null; close: () => void; }) => { - if (!baseContracts) { + if (!baseContracts || !daoAddress) { return; } const { safeSingletonContract } = baseContracts; const description = 'Add Signer'; - const calldatas = [ - safeSingletonContract.asSigner.interface.encodeFunctionData('addOwnerWithThreshold', [ - newSigner, - BigInt(threshold), - ]), - ]; + const encodedAddOwner = safeSingletonContract.asSigner.interface.encodeFunctionData( + 'addOwnerWithThreshold', + [newSigner, BigInt(threshold)], + ); + if (!isHex(encodedAddOwner)) { + return; + } + const calldatas = [encodedAddOwner]; const proposalData: ProposalExecuteData = { - targets: [daoAddress!], + targets: [getAddress(daoAddress)], values: [0n], - calldatas: calldatas, + calldatas, metaData: { title: 'Add Signer', description: description, diff --git a/src/components/pages/DaoSettings/components/Signers/hooks/useRemoveSigner.ts b/src/components/pages/DaoSettings/components/Signers/hooks/useRemoveSigner.ts index 763f1162bd..c18d402e35 100644 --- a/src/components/pages/DaoSettings/components/Signers/hooks/useRemoveSigner.ts +++ b/src/components/pages/DaoSettings/components/Signers/hooks/useRemoveSigner.ts @@ -1,5 +1,6 @@ import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; +import { isHex, getAddress } from 'viem'; import useSubmitProposal from '../../../../../../hooks/DAO/proposal/useSubmitProposal'; import { useFractal } from '../../../../../../providers/App/AppProvider'; import { ProposalExecuteData } from '../../../../../../types'; @@ -22,24 +23,27 @@ const useRemoveSigner = ({ const { baseContracts } = useFractal(); const removeSigner = useCallback(async () => { - if (!baseContracts) { + if (!baseContracts || !daoAddress) { return; } const { safeSingletonContract } = baseContracts; const description = 'Remove Signers'; - const calldatas = [ - safeSingletonContract.asProvider.interface.encodeFunctionData('removeOwner', [ - prevSigner, - signerToRemove, - BigInt(threshold), - ]), - ]; + const encodedRemoveOwner = safeSingletonContract.asProvider.interface.encodeFunctionData( + 'removeOwner', + [prevSigner, signerToRemove, BigInt(threshold)], + ); + + if (!isHex(encodedRemoveOwner)) { + return; + } + + const calldatas = [encodedRemoveOwner]; const proposalData: ProposalExecuteData = { - targets: [daoAddress!], + targets: [getAddress(daoAddress)], values: [0n], - calldatas: calldatas, + calldatas, metaData: { title: 'Remove Signers', description: description, diff --git a/src/components/ui/forms/InputComponent.tsx b/src/components/ui/forms/InputComponent.tsx index 217599379f..023de3e816 100644 --- a/src/components/ui/forms/InputComponent.tsx +++ b/src/components/ui/forms/InputComponent.tsx @@ -10,7 +10,6 @@ import { ResponsiveValue, } from '@chakra-ui/react'; import { LabelWrapper } from '@decent-org/fractal-ui'; -import { Address } from 'viem'; import { BigIntInput, BigIntInputProps } from './BigIntInput'; import { EthAddressInput } from './EthAddressInput'; @@ -38,7 +37,7 @@ interface InputProps extends Omit { } interface EthAddressProps extends Omit { - onAddressChange: (address: Address | undefined, isValid: boolean) => void; + onAddressChange: (address: string | undefined, isValid: boolean) => void; } interface TextareaProps extends Omit { diff --git a/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts b/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts index 6fdd1bd518..262740fe70 100644 --- a/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts +++ b/src/hooks/DAO/loaders/governance/useAzoriusListeners.ts @@ -8,6 +8,7 @@ import { import { VotedEvent as ERC20VotedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/LinearERC20Voting'; import { VotedEvent as ERC721VotedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/LinearERC721Voting'; import { Dispatch, useEffect, useMemo } from 'react'; +import { getAddress } from 'viem'; import { useFractal } from '../../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; import { useEthersProvider } from '../../../../providers/Ethers/hooks/useEthersProvider'; @@ -48,7 +49,11 @@ const proposalCreatedEventListener = ( return; } - const typedTransactions = transactions.map(t => ({ ...t, value: t.value.toBigInt() })); + const typedTransactions = transactions.map(t => ({ + ...t, + to: getAddress(t.to), + value: t.value.toBigInt(), + })); const metaDataEvent: CreateProposalMetadata = JSON.parse(metadata); const proposalData = { diff --git a/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts b/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts index ba2499618e..3ccdd8b7ce 100644 --- a/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts +++ b/src/hooks/DAO/loaders/governance/useAzoriusProposals.ts @@ -6,6 +6,7 @@ import { import { VotedEvent as ERC20VotedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/LinearERC20Voting'; import { VotedEvent as ERC721VotedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/LinearERC721Voting'; import { useCallback, useEffect, useMemo, useRef } from 'react'; +import { getAddress } from 'viem'; import { logError } from '../../../../helpers/errorLogging'; import { useFractal } from '../../../../providers/App/AppProvider'; import { useEthersProvider } from '../../../../providers/Ethers/hooks/useEthersProvider'; @@ -150,6 +151,7 @@ export const useAzoriusProposals = () => { _decode, proposalCreatedEvent.args.transactions.map(t => ({ ...t, + to: getAddress(t.to), value: t.value.toBigInt(), })), ); @@ -161,6 +163,7 @@ export const useAzoriusProposals = () => { }, transactions: proposalCreatedEvent.args.transactions.map(t => ({ ...t, + to: getAddress(t.to), value: t.value.toBigInt(), })), decodedTransactions, diff --git a/src/hooks/DAO/loaders/governance/useERC20LinearToken.ts b/src/hooks/DAO/loaders/governance/useERC20LinearToken.ts index e0759a269a..7603078425 100644 --- a/src/hooks/DAO/loaders/governance/useERC20LinearToken.ts +++ b/src/hooks/DAO/loaders/governance/useERC20LinearToken.ts @@ -1,5 +1,6 @@ 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'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; import useSafeContracts from '../../../safe/useSafeContracts'; @@ -32,7 +33,7 @@ export const useERC20LinearToken = ({ onMount = true }: { onMount?: boolean }) = name: tokenName, symbol: tokenSymbol, decimals: tokenDecimals, - address: votesTokenContractAddress, + address: getAddress(votesTokenContractAddress), totalSupply, }; isTokenLoaded.current = true; @@ -53,7 +54,7 @@ export const useERC20LinearToken = ({ onMount = true }: { onMount?: boolean }) = const tokenData = { name: tokenName, symbol: tokenSymbol, - address: underlyingTokenAddress, + address: getAddress(underlyingTokenAddress), }; action.dispatch({ type: FractalGovernanceAction.SET_UNDERLYING_TOKEN_DATA, diff --git a/src/hooks/DAO/loaders/governance/useERC721Tokens.ts b/src/hooks/DAO/loaders/governance/useERC721Tokens.ts index c93384341e..f857d5ed22 100644 --- a/src/hooks/DAO/loaders/governance/useERC721Tokens.ts +++ b/src/hooks/DAO/loaders/governance/useERC721Tokens.ts @@ -1,6 +1,6 @@ import { ERC721__factory } from '@fractal-framework/fractal-contracts'; import { useCallback } from 'react'; -import { zeroAddress } from 'viem'; +import { getAddress, zeroAddress } from 'viem'; import { logError } from '../../../../helpers/errorLogging'; import { useFractal } from '../../../../providers/App/AppProvider'; import { FractalGovernanceAction } from '../../../../providers/App/governance/action'; @@ -42,7 +42,7 @@ export default function useERC721Tokens() { } catch (e) { logError('Error while getting ERC721 total supply'); } - return { name, symbol, address, votingWeight, totalSupply }; + return { name, symbol, address: getAddress(address), votingWeight, totalSupply }; }), ); diff --git a/src/hooks/DAO/proposal/useCreateProposalTemplate.ts b/src/hooks/DAO/proposal/useCreateProposalTemplate.ts index 0fee5cdb6e..508a1b5837 100644 --- a/src/hooks/DAO/proposal/useCreateProposalTemplate.ts +++ b/src/hooks/DAO/proposal/useCreateProposalTemplate.ts @@ -1,4 +1,5 @@ import { useCallback } from 'react'; +import { isHex, getAddress } from 'viem'; import { useFractal } from '../../../providers/App/AppProvider'; import useIPFSClient from '../../../providers/App/hooks/useIPFSClient'; import { ProposalExecuteData } from '../../../types'; @@ -53,16 +54,19 @@ export default function useCreateProposalTemplate() { const { Hash } = await client.add(JSON.stringify(updatedTemplatesList)); + const encodedUpdateValues = keyValuePairsContract.asProvider.interface.encodeFunctionData( + 'updateValues', + [['proposalTemplates'], [Hash]], + ); + if (!isHex(encodedUpdateValues)) { + return; + } + const proposal: ProposalExecuteData = { metaData: proposalMetadata, - targets: [keyValuePairsContract.asProvider.address], + targets: [getAddress(keyValuePairsContract.asProvider.address)], values: [0n], - calldatas: [ - keyValuePairsContract.asProvider.interface.encodeFunctionData('updateValues', [ - ['proposalTemplates'], - [Hash], - ]), - ], + calldatas: [encodedUpdateValues], }; return proposal; diff --git a/src/hooks/DAO/proposal/usePrepareProposal.ts b/src/hooks/DAO/proposal/usePrepareProposal.ts index 4e60043391..917a876c1a 100644 --- a/src/hooks/DAO/proposal/usePrepareProposal.ts +++ b/src/hooks/DAO/proposal/usePrepareProposal.ts @@ -1,4 +1,5 @@ import { useCallback } from 'react'; +import { Hex, getAddress } from 'viem'; import { useEthersSigner } from '../../../providers/Ethers/hooks/useEthersSigner'; import { CreateProposalForm } from '../../../types/proposalBuilder'; import { encodeFunction } from '../../../utils/crypto'; @@ -13,7 +14,7 @@ export function usePrepareProposal() { if (!tx.functionName) { return { ...tx, - calldata: '0x', + calldata: '0x' as Hex, }; } else { const signature = tx.parameters.map(parameter => parameter.signature.trim()).join(', '); @@ -32,18 +33,18 @@ export function usePrepareProposal() { } }); const targets = await Promise.all( - transactionsWithEncoding.map(tx => { + transactionsWithEncoding.map(async tx => { if (couldBeENS(tx.targetAddress)) { - return signer!.resolveName(tx.targetAddress); + return getAddress(await signer!.resolveName(tx.targetAddress)); } - return tx.targetAddress; + return getAddress(tx.targetAddress); }), ); return { targets, values: transactionsWithEncoding.map(transaction => transaction.ethValue.bigintValue || 0n), - calldatas: transactionsWithEncoding.map(transaction => transaction.calldata || ''), + calldatas: transactionsWithEncoding.map(transaction => transaction.calldata || '0x'), metaData: { title: proposalMetadata.title, description: proposalMetadata.description, diff --git a/src/hooks/DAO/proposal/useRemoveProposalTemplate.ts b/src/hooks/DAO/proposal/useRemoveProposalTemplate.ts index 036cba697e..f50e3f0420 100644 --- a/src/hooks/DAO/proposal/useRemoveProposalTemplate.ts +++ b/src/hooks/DAO/proposal/useRemoveProposalTemplate.ts @@ -1,4 +1,5 @@ import { useCallback } from 'react'; +import { isHex, getAddress } from 'viem'; import { useFractal } from '../../../providers/App/AppProvider'; import useIPFSClient from '../../../providers/App/hooks/useIPFSClient'; import { ProposalExecuteData } from '../../../types'; @@ -27,16 +28,18 @@ export default function useRemoveProposalTemplate() { const { Hash } = await client.add(JSON.stringify(updatedTemplatesList)); + const encodedUpdateValues = keyValuePairsContract.asProvider.interface.encodeFunctionData( + 'updateValues', + [['proposalTemplates'], [Hash]], + ); + if (!isHex(encodedUpdateValues)) { + return; + } const proposal: ProposalExecuteData = { metaData: proposalMetadata, - targets: [keyValuePairsContract.asProvider.address], + targets: [getAddress(keyValuePairsContract.asProvider.address)], values: [0n], - calldatas: [ - keyValuePairsContract.asProvider.interface.encodeFunctionData('updateValues', [ - ['proposalTemplates'], - [Hash], - ]), - ], + calldatas: [encodedUpdateValues], }; return proposal; diff --git a/src/hooks/DAO/proposal/useSubmitProposal.ts b/src/hooks/DAO/proposal/useSubmitProposal.ts index 46c1e64114..32eed32d29 100644 --- a/src/hooks/DAO/proposal/useSubmitProposal.ts +++ b/src/hooks/DAO/proposal/useSubmitProposal.ts @@ -134,7 +134,7 @@ export default function useSubmitProposal() { return; } // Need to wrap it in Multisend function call - to = multiSendContract.asProvider.address; + to = getAddress(multiSendContract.asProvider.address); const tempData = proposalData.targets.map((target, index) => { return { diff --git a/src/hooks/DAO/useClawBack.ts b/src/hooks/DAO/useClawBack.ts index d026b41fa8..88012260af 100644 --- a/src/hooks/DAO/useClawBack.ts +++ b/src/hooks/DAO/useClawBack.ts @@ -45,8 +45,11 @@ export default function useClawBack({ childSafeInfo, parentAddress }: IUseClawBa 'execTx', [txData], ); + if (!isHex(fractalModuleCalldata)) { + throw new Error('Error encoding clawback call data'); + } return { - target: fractalModuleContract.address, + target: getAddress(fractalModuleContract.address), value: 0, calldata: fractalModuleCalldata, }; @@ -69,8 +72,12 @@ export default function useClawBack({ childSafeInfo, parentAddress }: IUseClawBa [txData], ); + if (!isHex(fractalModuleCalldata)) { + throw new Error('Error encoding clawback call data'); + } + return { - target: fractalModuleContract.address, + target: getAddress(fractalModuleContract.address), value: 0, calldata: fractalModuleCalldata, }; diff --git a/src/hooks/DAO/useCreateSubDAOProposal.ts b/src/hooks/DAO/useCreateSubDAOProposal.ts index 04e2005dbe..b553ac1929 100644 --- a/src/hooks/DAO/useCreateSubDAOProposal.ts +++ b/src/hooks/DAO/useCreateSubDAOProposal.ts @@ -1,5 +1,6 @@ import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; +import { isHex, getAddress } from 'viem'; import { useFractal } from '../../providers/App/AppProvider'; import { SafeMultisigDAO, AzoriusGovernance, AzoriusERC20DAO, AzoriusERC721DAO } from '../../types'; import { ProposalExecuteData } from '../../types/daoProposal'; @@ -38,18 +39,24 @@ export const useCreateSubDAOProposal = () => { const { safeTx, predictedSafeAddress } = builtSafeTx; + const encodedMultisend = multiSendContract.asProvider.interface.encodeFunctionData( + 'multiSend', + [safeTx], + ); + const encodedDeclareSubDAO = + fractalRegistryContract.asProvider.interface.encodeFunctionData('declareSubDAO', [ + predictedSafeAddress, + ]); + if (!isHex(encodedMultisend) || !isHex(encodedDeclareSubDAO)) { + return; + } const proposalData: ProposalExecuteData = { targets: [ - multiSendContract.asProvider.address, - fractalRegistryContract.asProvider.address, + getAddress(multiSendContract.asProvider.address), + getAddress(fractalRegistryContract.asProvider.address), ], values: [0n, 0n], - calldatas: [ - multiSendContract.asProvider.interface.encodeFunctionData('multiSend', [safeTx]), - fractalRegistryContract.asProvider.interface.encodeFunctionData('declareSubDAO', [ - predictedSafeAddress, - ]), - ], + calldatas: [encodedMultisend, encodedDeclareSubDAO], metaData: { title: t('Create a sub-Safe', { ns: 'proposalMetadata' }), description: '', diff --git a/src/hooks/DAO/useDeployAzorius.ts b/src/hooks/DAO/useDeployAzorius.ts index f2c970ebcf..d509ffb847 100644 --- a/src/hooks/DAO/useDeployAzorius.ts +++ b/src/hooks/DAO/useDeployAzorius.ts @@ -1,6 +1,7 @@ import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; +import { getAddress, isHex } from 'viem'; import { DAO_ROUTES } from '../../constants/routes'; import { TxBuilderFactory } from '../../models/TxBuilderFactory'; import { useFractal } from '../../providers/App/AppProvider'; @@ -99,16 +100,25 @@ const useDeployAzorius = () => { owners: safe.owners, }); + const encodedAddOwnerWithThreshold = + safeSingletonContract.asProvider.interface.encodeFunctionData('addOwnerWithThreshold', [ + multiSendContract.asProvider.address, + 1, + ]); + if (!isHex(encodedAddOwnerWithThreshold)) { + return; + } + const encodedMultisend = multiSendContract.asProvider.interface.encodeFunctionData( + 'multiSend', + [safeTx], + ); + if (!isHex(encodedMultisend)) { + return; + } const proposalData: ProposalExecuteData = { - targets: [daoAddress, multiSendContract.asProvider.address], + targets: [daoAddress, getAddress(multiSendContract.asProvider.address)], values: [0n, 0n], - calldatas: [ - safeSingletonContract.asProvider.interface.encodeFunctionData('addOwnerWithThreshold', [ - multiSendContract.asProvider.address, - 1, - ]), - multiSendContract.asProvider.interface.encodeFunctionData('multiSend', [safeTx]), - ], + calldatas: [encodedAddOwnerWithThreshold, encodedMultisend], metaData: { title: '', description: '', diff --git a/src/hooks/stake/lido/useLidoStaking.ts b/src/hooks/stake/lido/useLidoStaking.ts index 657587a5af..df8596df6a 100644 --- a/src/hooks/stake/lido/useLidoStaking.ts +++ b/src/hooks/stake/lido/useLidoStaking.ts @@ -1,6 +1,7 @@ import { getSTETHContract, getWithdrawalQueueContract } from '@lido-sdk/contracts'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; +import { isHex, getAddress } from 'viem'; import { useFractal } from '../../../providers/App/AppProvider'; import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; import { ProposalExecuteData } from '../../../types'; @@ -27,14 +28,20 @@ export default function useLidoStaking() { const stETHContract = getSTETHContract(lido.stETHContractAddress, signerOrProvider); + const encodedSubmit = stETHContract.interface.encodeFunctionData('submit', [ + lido.rewardsAddress, + ]); + if (!isHex(encodedSubmit)) { + return; + } const proposalData: ProposalExecuteData = { metaData: { title: t('Stake ETH with Lido'), description: t('This proposal will stake ETH in Lido, returning stETH to your treasury.'), documentationUrl: 'https://docs.lido.fi/guides/steth-integration-guide#what-is-steth', }, - targets: [lido.stETHContractAddress], - calldatas: [stETHContract.interface.encodeFunctionData('submit', [lido.rewardsAddress])], + targets: [getAddress(lido.stETHContractAddress)], + calldatas: [encodedSubmit], values: [value], }; await submitProposal({ @@ -60,6 +67,22 @@ export default function useLidoStaking() { signerOrProvider, ); + const encodedApprove = stETHContract.interface.encodeFunctionData('approve', [ + lido.withdrawalQueueContractAddress, + value, + ]); + + if (!isHex(encodedApprove)) { + return; + } + + const encodedWithdraw = withdrawalQueueContract.interface.encodeFunctionData( + 'requestWithdrawals', + [[value], daoAddress], + ); + if (!isHex(encodedWithdraw)) { + return; + } const proposalData: ProposalExecuteData = { metaData: { title: t('Unstake stETH'), @@ -69,17 +92,11 @@ export default function useLidoStaking() { documentationUrl: 'https://docs.lido.fi/guides/steth-integration-guide#request-withdrawal-and-mint-nft', }, - targets: [lido.stETHContractAddress, lido.withdrawalQueueContractAddress], - calldatas: [ - stETHContract.interface.encodeFunctionData('approve', [ - lido.withdrawalQueueContractAddress, - value, - ]), - withdrawalQueueContract.interface.encodeFunctionData('requestWithdrawals', [ - [value], - daoAddress, - ]), + targets: [ + getAddress(lido.stETHContractAddress), + getAddress(lido.withdrawalQueueContractAddress), ], + calldatas: [encodedApprove, encodedWithdraw], values: [0n, 0n], }; await submitProposal({ @@ -105,6 +122,12 @@ export default function useLidoStaking() { signerOrProvider, ); + const encodedClaim = withdrawalQueueContract.interface.encodeFunctionData('claimWithdrawal', [ + nftId, + ]); + if (!isHex(encodedClaim)) { + return; + } const proposalData: ProposalExecuteData = { metaData: { title: t('Lido Withdrawal'), @@ -113,10 +136,8 @@ export default function useLidoStaking() { ), documentationUrl: 'https://docs.lido.fi/guides/steth-integration-guide#claiming', }, - targets: [lido.withdrawalQueueContractAddress], - calldatas: [ - withdrawalQueueContract.interface.encodeFunctionData('claimWithdrawal', [nftId]), - ], + targets: [getAddress(lido.withdrawalQueueContractAddress)], + calldatas: [encodedClaim], values: [0n], }; await submitProposal({ diff --git a/src/types/daoProposal.ts b/src/types/daoProposal.ts index cc6bf6712b..ab59b14894 100644 --- a/src/types/daoProposal.ts +++ b/src/types/daoProposal.ts @@ -1,3 +1,4 @@ +import { Address, Hex } from 'viem'; import { GovernanceActivity } from './fractal'; import { CreateProposalMetadata } from './proposalBuilder'; import { SafeMultisigConfirmationResponse } from './safeGlobal'; @@ -8,9 +9,9 @@ export interface ProposalExecuteData extends ExecuteData { } export interface ExecuteData { - targets: string[]; + targets: Address[]; values: bigint[]; - calldatas: string[]; + calldatas: Hex[]; } export type CreateProposalFunc = (proposal: {