diff --git a/src/components/Proposals/ProposalSummary.tsx b/src/components/Proposals/ProposalSummary.tsx
index 11a99c710..6b8dc7623 100644
--- a/src/components/Proposals/ProposalSummary.tsx
+++ b/src/components/Proposals/ProposalSummary.tsx
@@ -1,6 +1,5 @@
-import { Box, Button, Flex, Icon, Text } from '@chakra-ui/react';
+import { Box, Button, Flex, Text } from '@chakra-ui/react';
import { abis } from '@fractal-framework/fractal-contracts';
-import { ArrowUpRight } from '@phosphor-icons/react';
import { format } from 'date-fns';
import { formatInTimeZone } from 'date-fns-tz';
import { useEffect, useMemo, useState } from 'react';
@@ -193,7 +192,7 @@ export function AzoriusProposalSummary({ proposal }: { proposal: AzoriusProposal
alignItems="center"
justifyContent="space-between"
>
- {format(eventDate, DEFAULT_DATE_TIME_FORMAT)}
+ {format(eventDate, DEFAULT_DATE_TIME_FORMAT)}
diff --git a/src/components/Roles/forms/RoleFormPaymentStreams.tsx b/src/components/Roles/forms/RoleFormPaymentStreams.tsx
index 397e9da7f..ea9a1b568 100644
--- a/src/components/Roles/forms/RoleFormPaymentStreams.tsx
+++ b/src/components/Roles/forms/RoleFormPaymentStreams.tsx
@@ -77,6 +77,25 @@ export function RoleFormPaymentStreams() {
{({ push: pushPayment }: { push: (streamFormValue: SablierPaymentFormValues) => void }) => (
+ }
+ iconSpacing={0}
+ onClick={async () => {
+ pushPayment({
+ isStreaming: () => false,
+ isCancellable: () => false,
+ isCancelling: false,
+ isValidatedAndSaved: false,
+ });
+ await validateForm();
+ setFieldValue('roleEditing.roleEditingPaymentIndex', (payments ?? []).length);
+ }}
+ >
+ {t('addPayment')}
+
{sortedPayments.length === 0 && (
)}
- }
- iconSpacing={0}
- onClick={async () => {
- pushPayment({
- isStreaming: () => false,
- isCancellable: () => false,
- isCancelling: false,
- isValidatedAndSaved: false,
- });
- await validateForm();
- setFieldValue('roleEditing.roleEditingPaymentIndex', (payments ?? []).length);
- }}
- >
- {t('addPayment')}
-
{!!sortedPayments.length && }
diff --git a/src/components/ui/cards/DAONodeInfoCard.tsx b/src/components/ui/cards/DAONodeInfoCard.tsx
index 508add2b9..52e3a1abe 100644
--- a/src/components/ui/cards/DAONodeInfoCard.tsx
+++ b/src/components/ui/cards/DAONodeInfoCard.tsx
@@ -96,7 +96,6 @@ export function DAONodeInfoCard(props: {
>
{/* Convert multisig badge casing here since it's already been cached to avoid another migration */}
{type === 'MULTISIG' ? `${type[0]}${type.slice(1).toLocaleLowerCase()}` : type}
- {type}
))}
diff --git a/src/components/ui/links/DisplayAddress.tsx b/src/components/ui/links/DisplayAddress.tsx
index bf67d84ad..152b318f8 100644
--- a/src/components/ui/links/DisplayAddress.tsx
+++ b/src/components/ui/links/DisplayAddress.tsx
@@ -1,5 +1,4 @@
-import { Flex, Text, Icon, LinkProps } from '@chakra-ui/react';
-import { ArrowUpRight } from '@phosphor-icons/react';
+import { Flex, Text, LinkProps } from '@chakra-ui/react';
import { ReactNode } from 'react';
import { Address } from 'viem';
import { useGetAccountName } from '../../../hooks/utils/useGetAccountName';
@@ -36,13 +35,6 @@ export function DisplayAddress({
>
{children || displayAddress.displayName}
- {!isTextLink && (
-
- )}
);
diff --git a/src/components/ui/links/DisplayTransaction.tsx b/src/components/ui/links/DisplayTransaction.tsx
index f0c1ecb1c..27af60140 100644
--- a/src/components/ui/links/DisplayTransaction.tsx
+++ b/src/components/ui/links/DisplayTransaction.tsx
@@ -1,31 +1,18 @@
-import { Flex, Text, Icon } from '@chakra-ui/react';
-import { ArrowUpRight } from '@phosphor-icons/react';
+import { Flex, Text } from '@chakra-ui/react';
import { createAccountSubstring } from '../../../hooks/utils/useGetAccountName';
import EtherscanLink from './EtherscanLink';
-export default function DisplayTransaction({
- txHash,
- isTextLink,
-}: {
- txHash: string;
- isTextLink?: boolean;
-}) {
+export default function DisplayTransaction({ txHash }: { txHash: string }) {
const displayName = createAccountSubstring(txHash);
return (
{displayName}
- {!isTextLink && (
-
- )}
);
diff --git a/src/components/ui/menus/CreateProposalMenu/index.tsx b/src/components/ui/menus/CreateProposalMenu/index.tsx
index d13f7b2dc..4652fea29 100644
--- a/src/components/ui/menus/CreateProposalMenu/index.tsx
+++ b/src/components/ui/menus/CreateProposalMenu/index.tsx
@@ -1,24 +1,11 @@
-import {
- Box,
- Button,
- Divider,
- Flex,
- Icon,
- Menu,
- MenuButton,
- MenuItem,
- MenuList,
- Text,
-} from '@chakra-ui/react';
+import { Button, Flex, Icon, Text } from '@chakra-ui/react';
import { CaretDown } from '@phosphor-icons/react';
-import { Fragment } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { Address } from 'viem';
-import { NEUTRAL_2_82_TRANSPARENT } from '../../../../constants/common';
import { DAO_ROUTES } from '../../../../constants/routes';
import { useNetworkConfigStore } from '../../../../providers/NetworkConfig/useNetworkConfigStore';
-import { EaseOutComponent } from '../../utils/EaseOutComponent';
+import { OptionMenu } from '../OptionMenu';
export function CreateProposalMenu({ safeAddress }: { safeAddress: Address }) {
const { t } = useTranslation('proposal');
@@ -28,102 +15,42 @@ export function CreateProposalMenu({ safeAddress }: { safeAddress: Address }) {
const navigate = useNavigate();
return (
-
-
-
+
+ {t('createProposal')}
+
+
+ }
+ options={[
+ {
+ optionKey: t('createFromScratch'),
+ onClick: () => navigate(DAO_ROUTES.proposalNew.relative(addressPrefix, safeAddress)),
+ },
+ {
+ optionKey: t('browseTemplates'),
+ onClick: () =>
+ navigate(DAO_ROUTES.proposalTemplates.relative(addressPrefix, safeAddress)),
+ },
+ ]}
+ namespace="proposal"
+ buttonAs={Button}
+ buttonProps={{
+ variant: 'tertiary',
+ paddingX: '0.5rem',
+ paddingY: '0.25rem',
+ _hover: { bg: 'neutral-2' },
+ _active: {
+ color: 'lilac-0',
+ bg: 'neutral-2',
+ },
+ }}
+ />
);
}
diff --git a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx
index 440222d94..72a807406 100644
--- a/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx
+++ b/src/components/ui/menus/ManageDAO/ManageDAOMenu.tsx
@@ -148,9 +148,9 @@ export function ManageDAOMenu() {
guard.userHasVotes
) {
if (type === GovernanceType.MULTISIG) {
- return [freezeOption, modifyGovernanceOption, settingsOption];
+ return [settingsOption, freezeOption, modifyGovernanceOption];
} else {
- return [freezeOption, settingsOption];
+ return [settingsOption, freezeOption];
}
} else if (
guard.freezeProposalCreatedTime !== null &&
@@ -163,20 +163,17 @@ export function ManageDAOMenu() {
module => module.moduleType === FractalModuleType.FRACTAL,
);
if (fractalModule) {
- return [clawBackOption, settingsOption];
+ return [settingsOption, clawBackOption];
} else {
return [settingsOption];
}
} else {
- const optionsArr = [];
- if (canUserCreateProposal) {
- if (type === GovernanceType.MULTISIG) {
- optionsArr.push(modifyGovernanceOption);
- }
- }
-
- optionsArr.push(settingsOption);
- return optionsArr;
+ return [
+ settingsOption,
+ ...(canUserCreateProposal && type === GovernanceType.MULTISIG
+ ? [modifyGovernanceOption]
+ : []),
+ ];
}
}, [
guard,
@@ -190,7 +187,26 @@ export function ManageDAOMenu() {
canUserCreateProposal,
]);
- return (
+ return options.length === 1 ? (
+
+ }
+ onClick={options[0].onClick}
+ variant="tertiary"
+ p="0.25rem"
+ h="fit-content"
+ sx={{
+ span: {
+ h: '1.25rem',
+ },
+ }}
+ />
+ ) : (
}
- titleKey={canUserCreateProposal ? 'titleManageDAO' : 'titleViewDAODetails'}
options={options}
namespace="menu"
buttonAs={IconButton}
diff --git a/src/components/ui/menus/OptionMenu/OptionsList.tsx b/src/components/ui/menus/OptionMenu/OptionsList.tsx
index 0c8112d8e..44017ae9c 100644
--- a/src/components/ui/menus/OptionMenu/OptionsList.tsx
+++ b/src/components/ui/menus/OptionMenu/OptionsList.tsx
@@ -12,7 +12,7 @@ export function OptionsList({
namespace,
titleKey,
}: IOptionsList) {
- const { t } = useTranslation(namespace);
+ const { t } = useTranslation(namespace || 'menu');
const createHandleItemClick =
(option: IOption) => (e: MouseEvent | ChangeEvent) => {
e.stopPropagation();
@@ -32,41 +32,47 @@ export function OptionsList({
)}
{options.map((option, i) => {
const clickListener = createHandleItemClick(option);
+
return (
-
+ {option.renderer ? (
+ option.renderer()
+ ) : (
+
+ )}
+
{i !== options.length - 1 && }
);
diff --git a/src/components/ui/menus/OptionMenu/index.tsx b/src/components/ui/menus/OptionMenu/index.tsx
index 7a0f78af9..66e5ad871 100644
--- a/src/components/ui/menus/OptionMenu/index.tsx
+++ b/src/components/ui/menus/OptionMenu/index.tsx
@@ -3,7 +3,6 @@ import { MouseEvent, ReactNode, RefObject } from 'react';
import { useTranslation } from 'react-i18next';
import { NEUTRAL_2_82_TRANSPARENT } from '../../../../constants/common';
import { DecentTooltip } from '../../DecentTooltip';
-import { EaseOutComponent } from '../../utils/EaseOutComponent';
import { OptionsList } from './OptionsList';
import { IOption, IOptionsList } from './types';
@@ -50,17 +49,15 @@ export function OptionMenu({
backdropFilter="auto"
backdropBlur="10px"
>
-
- {children}
-
-
+ {children}
+
);
diff --git a/src/components/ui/menus/OptionMenu/types.tsx b/src/components/ui/menus/OptionMenu/types.tsx
index 629966bca..ca2581715 100644
--- a/src/components/ui/menus/OptionMenu/types.tsx
+++ b/src/components/ui/menus/OptionMenu/types.tsx
@@ -3,6 +3,7 @@ export interface IOption {
count?: number;
onClick: () => void;
isSelected?: boolean;
+ renderer?: () => JSX.Element;
}
export interface IOptionsList {
@@ -10,6 +11,6 @@ export interface IOptionsList {
closeOnSelect?: boolean;
showOptionCount?: boolean;
showOptionSelected?: boolean;
- namespace: string;
+ namespace?: string;
titleKey?: string;
}
diff --git a/src/components/ui/menus/SafesMenu/SafeMenuItem.tsx b/src/components/ui/menus/SafesMenu/SafeMenuItem.tsx
index 2b3f9c124..16e43f0c5 100644
--- a/src/components/ui/menus/SafesMenu/SafeMenuItem.tsx
+++ b/src/components/ui/menus/SafesMenu/SafeMenuItem.tsx
@@ -1,4 +1,4 @@
-import { Box, Button, Flex, Image, MenuItem, Spacer, Text } from '@chakra-ui/react';
+import { Button, Flex, Image, MenuItem, Spacer, Text } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { Address } from 'viem';
@@ -28,38 +28,36 @@ export function SafeMenuItem({ address, network, name }: SafeMenuItemProps) {
};
return (
-
-
-
+
);
}
diff --git a/src/components/ui/menus/SafesMenu/index.tsx b/src/components/ui/menus/SafesMenu/index.tsx
index dc5f5d769..150abf884 100644
--- a/src/components/ui/menus/SafesMenu/index.tsx
+++ b/src/components/ui/menus/SafesMenu/index.tsx
@@ -1,22 +1,10 @@
-import {
- Box,
- Button,
- Flex,
- Hide,
- Icon,
- IconButton,
- Menu,
- MenuButton,
- Show,
- Text,
- useDisclosure,
-} from '@chakra-ui/react';
+import { Box, Button, Hide, Icon, IconButton, Show, Text, useDisclosure } from '@chakra-ui/react';
import { CaretDown, Star } from '@phosphor-icons/react';
-import { Fragment } from 'react';
import { useTranslation } from 'react-i18next';
+import { useAccountFavorites } from '../../../../hooks/DAO/loaders/useFavorites';
import { AllSafesDrawer } from '../../../../pages/home/AllSafesDrawer';
-import { EaseOutComponent } from '../../utils/EaseOutComponent';
-import { SafesList } from './SafesList';
+import { OptionMenu } from '../OptionMenu';
+import { SafeMenuItem } from './SafeMenuItem';
export function SafesMenu() {
const { t } = useTranslation('home');
@@ -26,6 +14,8 @@ export function SafesMenu() {
onClose: onSafesDrawerClose,
} = useDisclosure();
+ const { favoritesList } = useAccountFavorites();
+
return (
@@ -46,47 +36,48 @@ export function SafesMenu() {
-
+
+ {t('mySafes')}
+
+
+ }
+ options={favoritesList.map(favorite => ({
+ optionKey: `${favorite.networkPrefix}:${favorite.address}`,
+ onClick: () => {},
+ renderer: () => (
+
+ ),
+ }))}
+ buttonAs={Button}
+ buttonProps={{
+ variant: 'tertiary',
+ color: 'white-0',
+ _hover: { color: 'white-0', bg: 'neutral-3' },
+ _active: {
+ color: 'white-0',
+ bg: 'neutral-3',
+ },
+ paddingX: '0.75rem',
+ paddingY: '0.25rem',
+ }}
+ closeOnSelect={false}
+ showOptionSelected
+ showOptionCount
+ />
void }) {
- const { governance, governanceContracts } = useFractal();
- const azoriusGovernance = governance as AzoriusGovernance;
- const { data: walletClient } = useWalletClient();
- const publicClient = usePublicClient();
- const { address: account } = useAccount();
- const [userBalance, setUserBalance] = useState({
- value: '',
- bigintValue: 0n,
- });
-
- const { loadERC20TokenAccountData } = useERC20LinearToken({ onMount: false });
- const [contractCall, pending] = useTransaction();
- const {
- approved,
- approveTransaction,
- pending: approvalPending,
- } = useApproval(
- governanceContracts.underlyingTokenAddress,
- azoriusGovernance.votesToken?.address,
- userBalance.bigintValue,
- );
-
- const { t } = useTranslation(['modals', 'treasury']);
- const { restrictChars } = useFormHelpers();
-
- const getUserUnderlyingTokenBalance = useCallback(async () => {
- if (
- !azoriusGovernance.votesToken?.decimals ||
- !azoriusGovernance.votesToken.underlyingTokenData ||
- !publicClient ||
- !account
- )
- return;
- const baseTokenContract = getContract({
- address: azoriusGovernance.votesToken.underlyingTokenData.address,
- abi: erc20Abi,
- client: publicClient,
- });
- try {
- const [balance, decimals] = await Promise.all([
- baseTokenContract.read.balanceOf([account]),
- baseTokenContract.read.decimals(),
- ]);
- setUserBalance({
- value: formatCoin(
- balance,
- false,
- decimals,
- azoriusGovernance.votesToken?.underlyingTokenData?.symbol,
- ),
- bigintValue: balance,
- });
- } catch (e) {
- logError(e);
- return;
- }
- }, [account, azoriusGovernance.votesToken, publicClient]);
-
- useEffect(() => {
- getUserUnderlyingTokenBalance();
- }, [getUserUnderlyingTokenBalance]);
-
- const handleFormSubmit = useCallback(
- (amount: BigIntValuePair) => {
- const { votesTokenAddress } = governanceContracts;
- if (!votesTokenAddress || !account || !walletClient) return;
-
- const wrapperTokenContract = getContract({
- abi: abis.VotesERC20Wrapper,
- address: votesTokenAddress,
- client: walletClient,
- });
-
- contractCall({
- contractFn: () => wrapperTokenContract.write.depositFor([account, amount.bigintValue!]),
- pendingMessage: t('wrapTokenPendingMessage'),
- failedMessage: t('wrapTokenFailedMessage'),
- successMessage: t('wrapTokenSuccessMessage'),
- successCallback: async () => {
- await loadERC20TokenAccountData();
- },
- completedCallback: () => {
- close();
- },
- });
- },
- [account, contractCall, governanceContracts, close, t, loadERC20TokenAccountData, walletClient],
- );
-
- // @dev next couple of lines are written like this, to keep typing equivalent during the conversion from BN to bigint
- const userBalanceBigIntValue = userBalance.bigintValue;
- const userBalanceBigIntValueIsZero = userBalanceBigIntValue
- ? userBalanceBigIntValue === 0n
- : undefined;
-
- if (
- !azoriusGovernance.votesToken?.decimals ||
- !azoriusGovernance.votesToken.underlyingTokenData ||
- userBalanceBigIntValueIsZero
- ) {
- return null;
- }
-
- return (
- {
- const { amount } = values;
- handleFormSubmit(amount);
- }}
- validationSchema={Yup.object().shape({
- amount: Yup.object({
- value: Yup.string().required(),
- bigintValue: Yup.mixed().required(),
- }).test({
- name: 'Wrap Token Validation',
- message: t('wrapTokenError'),
- test: amount => {
- const amountBN = amount.bigintValue as bigint;
- if (!amount) return false;
-
- if (amountBN === 0n) return false;
- if (userBalance.bigintValue! === 0n) return false;
- if (amountBN > userBalance.bigintValue!) return false;
- return true;
- },
- }),
- })}
- >
- {({ handleSubmit, values, setFieldValue, errors }: FormikProps) => {
- return (
-
- );
- }}
-
- );
-}
diff --git a/src/components/ui/proposal/InfoRow.tsx b/src/components/ui/proposal/InfoRow.tsx
index 2d459fd78..ae938674b 100644
--- a/src/components/ui/proposal/InfoRow.tsx
+++ b/src/components/ui/proposal/InfoRow.tsx
@@ -23,23 +23,13 @@ export default function InfoRow({
{tooltip === undefined ? (
txHash ? (
-
+
) : (
{value}
)
) : (
- {txHash ? (
-
- ) : (
- {value}
- )}
+ {txHash ? : {value}}
)}
diff --git a/src/hooks/DAO/loaders/useHatsTree.ts b/src/hooks/DAO/loaders/useHatsTree.ts
index b87155a61..8e4ed5ff6 100644
--- a/src/hooks/DAO/loaders/useHatsTree.ts
+++ b/src/hooks/DAO/loaders/useHatsTree.ts
@@ -10,27 +10,23 @@ import { SablierV2LockupLinearAbi } from '../../../assets/abi/SablierV2LockupLin
import { useFractal } from '../../../providers/App/AppProvider';
import useIPFSClient from '../../../providers/App/hooks/useIPFSClient';
import { useNetworkConfigStore } from '../../../providers/NetworkConfig/useNetworkConfigStore';
-import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore';
import { DecentHatsError } from '../../../store/roles/rolesStoreUtils';
import { useRolesStore } from '../../../store/roles/useRolesStore';
import { SablierPayment } from '../../../types/roles';
import { convertStreamIdToBigInt } from '../../streams/useCreateSablierStream';
import { CacheExpiry, CacheKeys } from '../../utils/cache/cacheDefaults';
import { getValue, setValue } from '../../utils/cache/useLocalStorage';
-import { useParseSafeAddress } from '../useParseSafeAddress';
const hatsSubgraphClient = new HatsSubgraphClient({});
const useHatsTree = () => {
const { t } = useTranslation('roles');
- const { safeAddress } = useParseSafeAddress();
const {
governanceContracts: {
linearVotingErc20WithHatsWhitelistingAddress,
linearVotingErc721WithHatsWhitelistingAddress,
},
} = useFractal();
- const { safe } = useDaoInfoStore();
const {
hatsTreeId,
contextChainId,
@@ -297,10 +293,10 @@ const useHatsTree = () => {
}, [hatsTree, updateRolesWithStreams, getPaymentStreams, streamsFetched]);
useEffect(() => {
- if (safeAddress && safe?.address && safeAddress !== safe.address && hatsTree) {
+ if (!hatsTreeId && !!hatsTree) {
resetHatsStore();
}
- }, [resetHatsStore, safeAddress, safe?.address, hatsTree]);
+ }, [resetHatsStore, hatsTree, hatsTreeId]);
};
export { useHatsTree };
diff --git a/src/hooks/DAO/useKeyValuePairs.ts b/src/hooks/DAO/useKeyValuePairs.ts
index dd313a38a..87f7e93c4 100644
--- a/src/hooks/DAO/useKeyValuePairs.ts
+++ b/src/hooks/DAO/useKeyValuePairs.ts
@@ -1,7 +1,6 @@
import { abis } from '@fractal-framework/fractal-contracts';
import { hatIdToTreeId } from '@hatsprotocol/sdk-v1-core';
import { useEffect } from 'react';
-import { useSearchParams } from 'react-router-dom';
import { Address, GetContractEventsReturnType, getContract } from 'viem';
import { usePublicClient } from 'wagmi';
import { logError } from '../../helpers/errorLogging';
@@ -99,14 +98,11 @@ const useKeyValuePairs = () => {
contracts: { keyValuePairs, sablierV2LockupLinear },
} = useNetworkConfigStore();
const { setHatKeyValuePairData } = useRolesStore();
- const [searchParams] = useSearchParams();
const safeAddress = node.safe?.address;
useEffect(() => {
- const safeParam = searchParams.get('dao');
-
- if (!publicClient || !safeAddress || safeAddress !== safeParam?.split(':')[1]) {
+ if (!safeAddress || !publicClient) {
return;
}
@@ -160,7 +156,6 @@ const useKeyValuePairs = () => {
keyValuePairs,
safeAddress,
publicClient,
- searchParams,
setHatKeyValuePairData,
sablierV2LockupLinear,
]);
diff --git a/src/hooks/utils/useCreateRoles.ts b/src/hooks/utils/useCreateRoles.ts
index 6678f0398..6490f8349 100644
--- a/src/hooks/utils/useCreateRoles.ts
+++ b/src/hooks/utils/useCreateRoles.ts
@@ -915,6 +915,9 @@ export default function useCreateRoles() {
throw new Error('Cannot prepare transactions for edited role without smart address');
}
const newPredictedHatSmartAccount = await predictSmartAccount(BigInt(formHat.id));
+ if (!newPredictedHatSmartAccount) {
+ throw new Error('Cannot predict smart account');
+ }
const newStreamTxData = createBatchLinearStreamCreationTx(
newStreamsOnHat.map(stream => ({ ...stream, recipient: newPredictedHatSmartAccount })),
);
diff --git a/src/i18n/locales/en/menu.json b/src/i18n/locales/en/menu.json
index c7dfaa113..253f61f7e 100644
--- a/src/i18n/locales/en/menu.json
+++ b/src/i18n/locales/en/menu.json
@@ -3,8 +3,6 @@
"connectWallet": "Connect Wallet",
"disconnect": "Disconnect",
"wallet": "Wallet",
- "titleManageDAO": "Manage Safe",
- "titleViewDAODetails": "View Safe Details",
"titleManageProposalTemplate": "Manage Template",
"optionCreateSubDAO": "Create SubDAO",
"optionInitiateFreeze": "Initiate a Freeze",
diff --git a/src/pages/dao/settings/governance/SafeGovernanceSettingsPage.tsx b/src/pages/dao/settings/governance/SafeGovernanceSettingsPage.tsx
index 718c4d2e4..5841d80f4 100644
--- a/src/pages/dao/settings/governance/SafeGovernanceSettingsPage.tsx
+++ b/src/pages/dao/settings/governance/SafeGovernanceSettingsPage.tsx
@@ -44,7 +44,12 @@ export function SafeGovernanceSettingsPage() {
>
{(isERC20Governance || isERC721Governance) && (
- {t('daoSettingsGovernance')}
+
+ {t('daoSettingsGovernance')}
+
)}
diff --git a/src/pages/dao/settings/permissions/SafePermissionsSettingsPage.tsx b/src/pages/dao/settings/permissions/SafePermissionsSettingsPage.tsx
index cc4ad181d..bb331a6a2 100644
--- a/src/pages/dao/settings/permissions/SafePermissionsSettingsPage.tsx
+++ b/src/pages/dao/settings/permissions/SafePermissionsSettingsPage.tsx
@@ -11,6 +11,7 @@ import { BarLoader } from '../../../../components/ui/loaders/BarLoader';
import { ModalType } from '../../../../components/ui/modals/ModalProvider';
import { useDecentModal } from '../../../../components/ui/modals/useDecentModal';
import NestedPageHeader from '../../../../components/ui/page/Header/NestedPageHeader';
+import Divider from '../../../../components/ui/utils/Divider';
import { NEUTRAL_2_84 } from '../../../../constants/common';
import { DAO_ROUTES } from '../../../../constants/routes';
import { useCanUserCreateProposal } from '../../../../hooks/utils/useCanUserSubmitProposal';
@@ -88,17 +89,6 @@ export function SafePermissionsSettingsPage() {
display="flex"
bg={{ base: 'transparent', md: NEUTRAL_2_84 }}
>
- {canUserCreateProposal && (
- }
- width="max-content"
- onClick={openAddPermissionModal}
- >
- {t('addPermission')}
-
- )}
{!isLoaded ? (
)}
+
+ {canUserCreateProposal && (
+
+
+ }
+ width="max-content"
+ onClick={openAddPermissionModal}
+ alignSelf="flex-end"
+ >
+ {t('addPermission')}
+
+
+ )}
>
diff --git a/src/store/roles/rolesStoreUtils.ts b/src/store/roles/rolesStoreUtils.ts
index 5db97f48a..166bec4b6 100644
--- a/src/store/roles/rolesStoreUtils.ts
+++ b/src/store/roles/rolesStoreUtils.ts
@@ -134,7 +134,7 @@ export const predictAccountAddress = async (params: {
tokenId,
]);
if (!(await publicClient.getBytecode({ address: predictedAddress }))) {
- throw new DecentHatsError('Predicted address is not a contract');
+ return;
}
return predictedAddress;
};
@@ -288,6 +288,10 @@ export const sanitize = async (
publicClient,
});
+ if (!topHatSmartAddress) {
+ throw new DecentHatsError('Top Hat smart address is not valid');
+ }
+
const whitelistingVotingContract = whitelistingVotingStrategy
? getContract({
abi: abis.LinearERC20VotingWithHatsProposalCreation,
@@ -320,6 +324,9 @@ export const sanitize = async (
tokenId: BigInt(rawAdminHat.id),
publicClient,
});
+ if (!adminHatSmartAddress) {
+ throw new DecentHatsError('Admin Hat smart address is not valid');
+ }
const adminHat: DecentAdminHat = {
id: rawAdminHat.id,