Skip to content

Commit 0007b45

Browse files
authored
Merge pull request #40 from lidofinance/fix/create-vault
feat: readonly settings
2 parents c9ef80b + 80838d2 commit 0007b45

File tree

11 files changed

+145
-62
lines changed

11 files changed

+145
-62
lines changed

features/settings/main/components/controllers/data-field.tsx

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,15 @@ import { ReadonlyInput } from './readonly-input';
33
import { DisplayAddress } from './display-address';
44
import { GroupWrapper } from './styles';
55

6-
import { InputDataType } from 'features/settings/main/types';
7-
import { VaultInfo } from 'types';
6+
import type { MainSettingsOverview } from 'features/settings/main/types';
87
import { EditProperty } from './edit-property';
98
import { Text } from '@lidofinance/lido-ui';
9+
import {
10+
useVaultConfirmingRoles,
11+
useVaultPermissions,
12+
} from 'modules/vaults/hooks/use-vault-permissions';
1013

11-
interface InputResolverProps {
12-
name: string;
13-
label: string;
14-
editLabel: string;
15-
title: string;
16-
dataType: InputDataType;
17-
vaultKey: keyof VaultInfo;
18-
actionText?: string;
19-
}
14+
type InputResolverProps = MainSettingsOverview;
2015

2116
export const DataField: FC<InputResolverProps> = ({
2217
label,
@@ -26,17 +21,30 @@ export const DataField: FC<InputResolverProps> = ({
2621
title,
2722
actionText = 'Initiate a change',
2823
vaultKey,
24+
canEditRole,
2925
}) => {
3026
const isTypeAddress = dataType === 'address';
27+
const isConfirmingRoles = canEditRole === 'confirmingRoles';
28+
const { hasConfirmingRole } = useVaultConfirmingRoles();
29+
const { hasPermission } = useVaultPermissions(
30+
isConfirmingRoles ? undefined : canEditRole,
31+
);
3132

33+
const isEditable = hasConfirmingRole || hasPermission;
3234
return (
3335
<GroupWrapper>
3436
<Text size="xs" strong>
3537
{title}
3638
</Text>
3739
{isTypeAddress && <DisplayAddress name={name} vaultKey={vaultKey} />}
3840
{!isTypeAddress && <ReadonlyInput label={label} vaultKey={vaultKey} />}
39-
<EditProperty editLabel={editLabel} name={name} actionText={actionText} />
41+
{isEditable && (
42+
<EditProperty
43+
editLabel={editLabel}
44+
name={name}
45+
actionText={actionText}
46+
/>
47+
)}
4048
</GroupWrapper>
4149
);
4250
};

features/settings/main/consts.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export const fieldsForRender: MainSettingsOverview[] = [
5858
editLabel: 'Propose new Node Operator fee, %',
5959
dataType: 'percent',
6060
vaultKey: 'nodeOperatorFeeBP',
61+
canEditRole: 'confirmingRoles',
6162
},
6263
{
6364
name: 'confirmExpiry',
@@ -66,6 +67,7 @@ export const fieldsForRender: MainSettingsOverview[] = [
6667
editLabel: 'Propose new Сonfirmation Lifetime, hours',
6768
dataType: 'time',
6869
vaultKey: 'confirmExpiry',
70+
canEditRole: 'confirmingRoles',
6971
},
7072
{
7173
name: 'defaultAdmin',
@@ -75,6 +77,7 @@ export const fieldsForRender: MainSettingsOverview[] = [
7577
dataType: 'address',
7678
actionText: 'Add new address',
7779
vaultKey: 'defaultAdmins',
80+
canEditRole: 'defaultAdmin',
7881
},
7982
{
8083
name: 'nodeOperatorManager',
@@ -84,5 +87,6 @@ export const fieldsForRender: MainSettingsOverview[] = [
8487
dataType: 'address',
8588
actionText: 'Add new address',
8689
vaultKey: 'nodeOperatorManagers',
90+
canEditRole: 'nodeOperatorManager',
8791
},
8892
];

features/settings/main/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Address, Hash, Hex } from 'viem';
22
import { editMainSettingsSchema, SubmittingMainFormStepsEnum } from './consts';
33
import { z } from 'zod';
44
import { VaultInfo } from 'types';
5+
import { VAULT_ROOT_ROLES } from 'modules/vaults';
56

67
export type SubmittingMainFormStep =
78
| keyof typeof SubmittingMainFormStepsEnum
@@ -34,6 +35,7 @@ export type MainSettingsOverview = {
3435
dataType: InputDataType;
3536
actionText?: string;
3637
vaultKey: keyof VaultInfo;
38+
canEditRole: VAULT_ROOT_ROLES | 'confirmingRoles';
3739
};
3840

3941
export type TxData = {

features/settings/permissions/components/address-list/address-block/address-block.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,15 @@ import {
99
PermissionKeys,
1010
} from 'features/settings/permissions/types';
1111

12-
export interface AddressBlockProps {
12+
export type AddressBlockProps = {
1313
permission: PermissionKeys;
14-
}
14+
readonly?: boolean;
15+
};
1516

16-
export const AddressBlock: FC<AddressBlockProps> = ({ permission }) => {
17+
export const AddressBlock: FC<AddressBlockProps> = ({
18+
permission,
19+
readonly,
20+
}) => {
1721
const { watch } = useFormContext();
1822
const fields = (watch(permission) ?? []) as FieldSchema[];
1923

@@ -25,6 +29,7 @@ export const AddressBlock: FC<AddressBlockProps> = ({ permission }) => {
2529
key={field.account}
2630
field={field}
2731
index={index}
32+
readonly={readonly}
2833
permission={permission}
2934
/>
3035
);

features/settings/permissions/components/address-list/address-item/address-item.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,18 @@ import {
1616
} from 'features/settings/permissions/types';
1717
import { useFormContext } from 'react-hook-form';
1818

19-
export interface AddressItemProps {
19+
export type AddressItemProps = {
2020
index: number;
2121
field: FieldSchema;
2222
permission: PermissionKeys;
23-
}
23+
readonly?: boolean;
24+
};
2425

2526
export const AddressItem: FC<AddressItemProps> = ({
2627
index,
2728
field,
2829
permission,
30+
readonly,
2931
}) => {
3032
const { setValue } = useFormContext();
3133
const { account, state, group } = field;
@@ -85,6 +87,7 @@ export const AddressItem: FC<AddressItemProps> = ({
8587
address={account}
8688
crossedText={isTextCrossed}
8789
bgColor={bgColor}
90+
readonly={readonly}
8891
onToggle={handleUpdateFormItem}
8992
onClick={handleShowPopover}
9093
/>

features/settings/permissions/components/address-list/address-list.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ import { PermissionKeys } from 'features/settings/permissions/types';
1010

1111
export type AddressListProps = {
1212
permission: PermissionKeys;
13+
readonly?: boolean;
1314
};
1415

15-
export const AddressList: FC<AddressListProps> = ({ permission }) => {
16+
export const AddressList: FC<AddressListProps> = (props) => {
1617
return (
1718
<AddressListWrapper>
18-
<AddressBlock permission={permission} />
19-
<InputBlock permission={permission} />
19+
<AddressBlock {...props} />
20+
<InputBlock {...props} />
2021
</AddressListWrapper>
2122
);
2223
};

features/settings/permissions/components/address-list/input-block/input-block.tsx

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ import type {
1313
PermissionKeys,
1414
} from 'features/settings/permissions/types';
1515

16-
export interface InputBlockProps {
16+
export type InputBlockProps = {
1717
permission: PermissionKeys;
18-
}
18+
readonly?: boolean;
19+
};
1920

20-
export const InputBlock: FC<InputBlockProps> = ({ permission }) => {
21+
export const InputBlock: FC<InputBlockProps> = ({ permission, readonly }) => {
2122
const { getValues } = useFormContext();
2223

2324
const {
@@ -54,16 +55,18 @@ export const InputBlock: FC<InputBlockProps> = ({ permission }) => {
5455
/>
5556
))}
5657

57-
<AddAddress
58-
color="primary"
59-
icon={<Plus />}
60-
size="md"
61-
variant="ghost"
62-
type="button"
63-
onClick={() => append({ value: '' })}
64-
>
65-
Add new address
66-
</AddAddress>
58+
{!readonly && (
59+
<AddAddress
60+
color="primary"
61+
icon={<Plus />}
62+
size="md"
63+
variant="ghost"
64+
type="button"
65+
onClick={() => append({ value: '' })}
66+
>
67+
Add new address
68+
</AddAddress>
69+
)}
6770
</InputBlockWrapper>
6871
);
6972
};

features/settings/permissions/permissions.tsx

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,47 +16,60 @@ import { PermissionsDataProvider, PermissionsFormProvider } from './contexts';
1616
import {
1717
VAULT_MANAGER_PERMISSIONS_LIST,
1818
NO_MANAGER_PERMISSION_LIST,
19+
VAULT_ROOT_ROLES,
1920
} from 'modules/vaults';
21+
import { useVaultPermissions } from 'modules/vaults/hooks/use-vault-permissions';
2022

21-
interface RenderPermissions {
23+
type PermissionSectionEntry = {
2224
permissionsTitle: string;
2325
payload: PermissionsRoles[];
24-
}
26+
canEditRole: VAULT_ROOT_ROLES;
27+
};
2528

26-
const renderPermissionsList: RenderPermissions[] = [
29+
const renderPermissionsList: PermissionSectionEntry[] = [
2730
{
2831
permissionsTitle: 'Vault Manager Permissions',
32+
canEditRole: 'defaultAdmin',
2933
payload: VAULT_MANAGER_PERMISSIONS_LIST,
3034
},
3135
{
3236
permissionsTitle: 'Node Operator Manager Permissions',
37+
canEditRole: 'nodeOperatorManager',
3338
payload: NO_MANAGER_PERMISSION_LIST,
3439
},
3540
];
3641

42+
const PermissionsSection = (props: PermissionSectionEntry) => {
43+
const { hasPermission } = useVaultPermissions(props.canEditRole);
44+
45+
return (
46+
<PermissionContainer>
47+
<PermissionGroupTitle>{props.permissionsTitle}</PermissionGroupTitle>
48+
<PermissionBlock>
49+
{props.payload.map(({ role, title, tooltip }) => {
50+
return (
51+
<PermissionRoleWrapper key={role}>
52+
<RoleDescription
53+
permission={role}
54+
description={title}
55+
tooltip={tooltip}
56+
/>
57+
<AddressList readonly={!hasPermission} permission={role} />
58+
</PermissionRoleWrapper>
59+
);
60+
})}
61+
</PermissionBlock>
62+
</PermissionContainer>
63+
);
64+
};
65+
3766
export const PermissionsSettings = () => {
3867
return (
3968
<PermissionsDataProvider>
4069
<PermissionsFormProvider>
4170
<SectionContainer>
42-
{renderPermissionsList.map(({ permissionsTitle, payload }) => (
43-
<PermissionContainer key={permissionsTitle}>
44-
<PermissionGroupTitle>{permissionsTitle}</PermissionGroupTitle>
45-
<PermissionBlock>
46-
{payload.map(({ role, title, tooltip }) => {
47-
return (
48-
<PermissionRoleWrapper key={role}>
49-
<RoleDescription
50-
permission={role}
51-
description={title}
52-
tooltip={tooltip}
53-
/>
54-
<AddressList permission={role} />
55-
</PermissionRoleWrapper>
56-
);
57-
})}
58-
</PermissionBlock>
59-
</PermissionContainer>
71+
{renderPermissionsList.map((section) => (
72+
<PermissionsSection key={section.permissionsTitle} {...section} />
6073
))}
6174
<PermissionsAction />
6275
</SectionContainer>

modules/vaults/consts/roles.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export const VAULTS_ROOT_ROLES_MAP = {
1515
),
1616
} as const;
1717

18+
export type VAULT_ROOT_ROLES = keyof typeof VAULTS_ROOT_ROLES_MAP;
19+
1820
export const VAULTS_OWNER_ROLES_MAP = {
1921
supplier: toRoleHash('vaults.Permissions.Fund'),
2022
withdrawer: toRoleHash('vaults.Permissions.Withdraw'),
@@ -47,13 +49,17 @@ export const VAULTS_OWNER_ROLES_MAP = {
4749
assetRecoverer: toRoleHash('vaults.Dashboard.RecoverAssets'),
4850
} as const;
4951

52+
export type VAULT_OWNER_ROLES = keyof typeof VAULTS_OWNER_ROLES_MAP;
53+
5054
export const VAULTS_NO_ROLES_MAP = {
5155
nodeOperatorFeeClaimer: toRoleHash('vaults.NodeOperatorFee.FeeClaimRole'),
5256
nodeOperatorRewardsAdjuster: toRoleHash(
5357
'vaults.NodeOperatorFee.RewardsAdjustRole',
5458
),
5559
} as const;
5660

61+
export type VAULTS_NO_ROLES = keyof typeof VAULTS_NO_ROLES_MAP;
62+
5763
export const VAULT_MANAGER_PERMISSIONS_LIST: PermissionEntry<
5864
typeof VAULTS_OWNER_ROLES_MAP
5965
>[] = [

modules/vaults/hooks/use-vault-permissions.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import type { Address, Hash } from 'viem';
77

88
export type DashboardRoles = keyof typeof VAULTS_ALL_ROLES_MAP;
99

10-
export const useVaultPermissions = (role: DashboardRoles) => {
10+
export const useVaultPermissions = (role?: DashboardRoles) => {
1111
const { activeVault } = useVaultInfo();
1212
const { address } = useAccount();
1313

14-
const roleHash = VAULTS_ALL_ROLES_MAP[role];
14+
const roleHash = role && VAULTS_ALL_ROLES_MAP[role];
1515

1616
const query = useReadContract({
1717
abi: dashboardAbi,
@@ -29,3 +29,19 @@ export const useVaultPermissions = (role: DashboardRoles) => {
2929
...query,
3030
};
3131
};
32+
33+
export const useVaultConfirmingRoles = () => {
34+
// TODO: multicall/useReadContracts
35+
const roleAdmin = useVaultPermissions('defaultAdmin');
36+
const roleNOM = useVaultPermissions('nodeOperatorManager');
37+
38+
const hasAtLeastOne = roleAdmin.hasPermission || roleNOM.hasPermission;
39+
return {
40+
hasConfirmingRole: hasAtLeastOne,
41+
hasAdmin: roleAdmin.hasPermission,
42+
hasNodeOperatporManager: roleNOM.hasPermission,
43+
hasBothConfirmingRoles: roleAdmin.hasPermission && roleNOM.hasPermission,
44+
isLoading: !hasAtLeastOne && (roleAdmin.isLoading || roleNOM.isLoading),
45+
error: roleAdmin.error || roleNOM.error,
46+
};
47+
};

0 commit comments

Comments
 (0)