Skip to content

Commit 2932ad4

Browse files
authored
Merge pull request #223 from threshold-network/operator-mapping-code-cleaning
Operator mapping - code cleaning Closes #232 (64677b3) Closes #255 (ae4231d) # Operator mapping code cleaning ### Main changes: - rearrange the operator mapping related data in redux store - adds operator and staking provider addresses as props to the Operator Mapping related modals and `OperatorAddressMappingCard` - removes unnecessary `shouldDisplayMapOperatorToStakingProviderModal` effect - fix some of the review comments from #199 - fix a bug that caused the `MapOperatorToStakingProvider` modal to not be displayed when we changed accounts in MetaMask - update text in operator mapping modal (Closes: #232) TODO in the future: - [ ] display Operator Mapping Card when the operator is mapped - #259 - [ ] display Operator Mapping Card and the modals related to it after submitting a stake (without the need to refresh) # Change details ### rearrange the operator mapping related data in redux store Previously we keep the data needed to operator mapping in the redux store in this format: (1) `rolesOf` property in `connected-account` slice: ``` { address: string, rolesOf: { owner: string, authorizer: string, beneficiary: string, } ``` This indicated if the currently logged account is used as a staking provider in other stakes. We fetched the data from the contract. (2) `mappedOperator` property in `staking-applications` slice: ``` { tbtc: { parameter: (...), stakingProviders: (...), mappedOperator: { isInitialFetchDone: boolean, isFetching: boolean, error: string, data: string, }, randomBeacon: { parameter: (...), stakingProviders: (...), mappedOperator: { isInitialFetchDone: boolean, isFetching: boolean, error: string, data: string, } } ``` This stored the data related to currently logged staking provider and it's mapped operator for each application. This could wrongly indicate that the data is more related to the staking app itself (since it was in the `staking-application` slice) when in reality it was more related to currently connected account. At first the idea was to not store this data in redux store at all, but unfrotunately this didn't went as smooth as I thogught and I think we must store them mainly to properly display `OperatorAddressMappingCard` in the staking page for all staking providers that don't have the operator mapped. I've encoutered problems with implementing it without using the store when the staking provider !=== owner. This is why i decided to go back to keeping the data in the redux store (you can see all this in the commit history) but in better (and i think simpler) form. Right now we will store this data in `account` slice and we ditched the whole `rolesOf` property which was object, to just `isStakingProvider` which is boolean value. After the changes the `connected-account` is renamed to `account` and looks like this: ``` { address: string, isStakingProvider: boolean, operatorMapping: { data: { tbtc: string, randomBeacon: string, }, isFetching: boolean, isInitialFetchDone: boolean, }, } ``` ### adds operator and staking provider addresses as props to the Operator Mapping related modals and `OperatorAddressMappingCard` For these components we are adding a props that contains operator address and staking provider address: - `OperatorAddressMappingCard` - `MapOperatorToStakingProviderModal` - `MapOperatorToStakingProviderSuccessModal` - `MapOperatorToStakingProviderConfirmationModal` - `MapOperatorToStakingProviderForm` ### removes unnecessary shouldDisplayMapOperatorToStakingProviderModal effect This effect was unnecessary. Instead of using `predicate` (from redux-toolkit) when calling `displayMapOperatorToStakingProviderModalEffect` we now use `actionCreator` (also from redux-toolkit) to call this effect on `operatorMappingInitialFetchDone` action call which in turn is called by `setStakes` action. This way we are sure we have all needed that in redux store and we can use them in ` displayMapOperatorToStakingProviderModalEffect` ### fix some of the review comments from #199 Most comment were either fixed by itself during the refactor process (the part of code that was commented does no longer exist) and other ones I've tried to fix. For some I've added comments there to start a discussion. ### fix a bug that caused the `MapOperatorToStakingProvider` modal to not be displayed when we changed accounts in MetaMask We are setting the `isSuccessfullLoginModalClosed` flag to false when resetting the store now, because the reset happend when we change the accounts and the login modal is not displayed in such case. We also set `isMappingOperatorToStakingProviderModalClosed` flag back to false in this case. **Additional note**: The modal queue was also moved in the store to `modal` prop since it's related to modal. In the furue we are planning to create a real modal queue. ### update text in operator mapping modal Update text based on Figma designs in `OperatorAddressMappingCard` component and in `MapOperatorToStakingProviderModal`. For more details see #232
2 parents 9d4ce43 + 96105de commit 2932ad4

File tree

27 files changed

+556
-584
lines changed

27 files changed

+556
-584
lines changed

src/components/Modal/MapOperatorToStakingProviderConfirmationModal/index.tsx

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,19 @@ import {
99
ModalHeader,
1010
} from "@chakra-ui/react"
1111
import { AddressZero } from "@ethersproject/constants"
12-
import {
13-
BodyLg,
14-
BodyMd,
15-
BodySm,
16-
H5,
17-
LabelSm,
18-
} from "@threshold-network/components"
12+
import { BodyLg, BodyMd, H5, LabelSm } from "@threshold-network/components"
1913
import { useWeb3React } from "@web3-react/core"
2014
import { ContractTransaction } from "ethers"
2115
import { FC, useCallback } from "react"
22-
import { useDispatch } from "react-redux"
2316
import { ModalType } from "../../../enums"
24-
import { useOperatorMappedtoStakingProviderHelpers } from "../../../hooks/staking-applications/useOperatorMappedToStakingProviderHelpers"
2517
import { useRegisterMultipleOperatorsTransaction } from "../../../hooks/staking-applications/useRegisterMultipleOperatorsTransaction"
2618
import { useRegisterOperatorTransaction } from "../../../hooks/staking-applications/useRegisterOperatorTransaction"
19+
import { useAppDispatch } from "../../../hooks/store"
2720
import { useModal } from "../../../hooks/useModal"
2821
import StakeAddressInfo from "../../../pages/Staking/StakeCard/StakeAddressInfo"
29-
import { mapOperatorToStakingProviderModalClosed } from "../../../store/modalQueue"
22+
import { mapOperatorToStakingProviderModalClosed } from "../../../store/modal"
3023
import { BaseModalProps } from "../../../types"
24+
import { isAddressZero } from "../../../web3/utils"
3125
import InfoBox from "../../InfoBox"
3226
import withBaseModal from "../withBaseModal"
3327

@@ -60,17 +54,27 @@ const OperatorMappingConfirmation: FC<
6054
const MapOperatorToStakingProviderConfirmationModal: FC<
6155
BaseModalProps & {
6256
operator: string
57+
mappedOperatorTbtc: string
58+
mappedOperatorRandomBeacon: string
6359
}
64-
> = ({ operator, closeModal }) => {
60+
> = ({
61+
operator,
62+
mappedOperatorTbtc,
63+
mappedOperatorRandomBeacon,
64+
closeModal,
65+
}) => {
6566
const { account } = useWeb3React()
6667
const { registerMultipleOperators } =
6768
useRegisterMultipleOperatorsTransaction()
68-
const dispatch = useDispatch()
69+
const dispatch = useAppDispatch()
70+
71+
const isOperatorMappedOnlyInTbtc =
72+
!isAddressZero(mappedOperatorTbtc) &&
73+
isAddressZero(mappedOperatorRandomBeacon)
6974

70-
const operatorMappedToStakingProviderHelpers =
71-
useOperatorMappedtoStakingProviderHelpers()
72-
const { isOperatorMappedOnlyInRandomBeacon, isOperatorMappedOnlyInTbtc } =
73-
operatorMappedToStakingProviderHelpers
75+
const isOperatorMappedOnlyInRandomBeacon =
76+
isAddressZero(mappedOperatorTbtc) &&
77+
!isAddressZero(mappedOperatorRandomBeacon)
7478

7579
const { openModal } = useModal()
7680
const onSuccess = useCallback(

src/components/Modal/MapOperatorToStakingProviderModal/MapOperatorToStakingProviderForm.tsx

Lines changed: 58 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { FC, Ref } from "react"
22
import { FormikProps, FormikErrors, withFormik } from "formik"
33
import { Form, FormikInput } from "../../Forms"
44
import { getErrorsObj, validateETHAddress } from "../../../utils/forms"
5-
import { OperatorMappedToStakingProviderHelpers } from "../../../hooks/staking-applications/useOperatorMappedToStakingProviderHelpers"
5+
import { isAddressZero, isSameETHAddress } from "../../../web3/utils"
66

77
export interface MapOperatorToStakingProviderFormValues {
88
operator: string
@@ -26,10 +26,58 @@ const MapOperatorToStakingProviderFormBase: FC<
2626
)
2727
}
2828

29+
const validateInputtedOperatorAddress = async (
30+
operator: string,
31+
checkIfOperatorIsMappedToAnotherStakingProvider: (
32+
operator: string
33+
) => Promise<boolean>,
34+
mappedOperatorRandomBeacon: string,
35+
mappedOperatorTbtc: string
36+
): Promise<string | undefined> => {
37+
let validationMsg: string | undefined = ""
38+
39+
const isOperatorMappedOnlyInTbtc =
40+
!isAddressZero(mappedOperatorTbtc) &&
41+
isAddressZero(mappedOperatorRandomBeacon)
42+
43+
const isOperatorMappedOnlyInRandomBeacon =
44+
isAddressZero(mappedOperatorTbtc) &&
45+
!isAddressZero(mappedOperatorRandomBeacon)
46+
47+
try {
48+
const isOperatorMappedToAnotherStakingProvider =
49+
await checkIfOperatorIsMappedToAnotherStakingProvider(operator)
50+
validationMsg = undefined
51+
if (isOperatorMappedToAnotherStakingProvider) {
52+
validationMsg = "Operator is already mapped to another staking provider."
53+
}
54+
if (
55+
isOperatorMappedOnlyInRandomBeacon &&
56+
!isSameETHAddress(operator, mappedOperatorRandomBeacon)
57+
) {
58+
validationMsg =
59+
"The operator address doesn't match the one used in random beacon app"
60+
}
61+
if (
62+
isOperatorMappedOnlyInTbtc &&
63+
!isSameETHAddress(operator, mappedOperatorTbtc)
64+
) {
65+
validationMsg =
66+
"The operator address doesn't match the one used in tbtc app"
67+
}
68+
} catch (error) {
69+
console.error("`MapOperatorToStakingProviderForm` validation error.", error)
70+
validationMsg = (error as Error)?.message
71+
}
72+
73+
return validationMsg
74+
}
75+
2976
type MapOperatorToStakingProviderFormProps = {
3077
initialAddress: string
78+
mappedOperatorTbtc: string
79+
mappedOperatorRandomBeacon: string
3180
innerRef: Ref<FormikProps<MapOperatorToStakingProviderFormValues>>
32-
operatorMappedToStakingProviderHelpers: OperatorMappedToStakingProviderHelpers
3381
checkIfOperatorIsMappedToAnotherStakingProvider: (
3482
operator: string
3583
) => Promise<boolean>
@@ -45,50 +93,20 @@ const MapOperatorToStakingProviderForm = withFormik<
4593
}),
4694
validate: async (values, props) => {
4795
const {
96+
mappedOperatorTbtc,
97+
mappedOperatorRandomBeacon,
4898
checkIfOperatorIsMappedToAnotherStakingProvider,
49-
operatorMappedToStakingProviderHelpers,
5099
} = props
51-
const {
52-
operatorMappedRandomBeacon,
53-
operatorMappedTbtc,
54-
isOperatorMappedOnlyInRandomBeacon,
55-
isOperatorMappedOnlyInTbtc,
56-
} = operatorMappedToStakingProviderHelpers
57100
const errors: FormikErrors<MapOperatorToStakingProviderFormValues> = {}
58101

59102
errors.operator = validateETHAddress(values.operator)
60103
if (!errors.operator) {
61-
let validationMsg: string | undefined = ""
62-
try {
63-
const isOperatorMappedToAnotherStakingProvider =
64-
await checkIfOperatorIsMappedToAnotherStakingProvider(values.operator)
65-
validationMsg = undefined
66-
if (isOperatorMappedToAnotherStakingProvider) {
67-
validationMsg =
68-
"Operator is already mapped to another staking provider."
69-
}
70-
if (
71-
isOperatorMappedOnlyInRandomBeacon &&
72-
values.operator !== operatorMappedRandomBeacon
73-
) {
74-
validationMsg =
75-
"The operator address doesn't match the one used in tbtc app"
76-
}
77-
if (
78-
isOperatorMappedOnlyInTbtc &&
79-
values.operator !== operatorMappedTbtc
80-
) {
81-
validationMsg =
82-
"The operator address doesn't match the one used in random beacon app"
83-
}
84-
} catch (error) {
85-
console.error(
86-
"`MapOperatorToStakingProviderForm` validation error.",
87-
error
88-
)
89-
validationMsg = (error as Error)?.message
90-
}
91-
errors.operator = validationMsg
104+
errors.operator = await validateInputtedOperatorAddress(
105+
values.operator,
106+
checkIfOperatorIsMappedToAnotherStakingProvider,
107+
mappedOperatorRandomBeacon,
108+
mappedOperatorTbtc
109+
)
92110
}
93111

94112
return getErrorsObj(errors)

src/components/Modal/MapOperatorToStakingProviderModal/index.tsx

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
Box,
88
Button,
99
H5,
10-
HStack,
1110
LabelSm,
1211
ModalBody,
1312
ModalCloseButton,
@@ -25,32 +24,43 @@ import { ModalType } from "../../../enums"
2524
import { useModal } from "../../../hooks/useModal"
2625
import StakeAddressInfo from "../../../pages/Staking/StakeCard/StakeAddressInfo"
2726
import { useWeb3React } from "@web3-react/core"
28-
import { AddressZero } from "@ethersproject/constants"
2927
import { useThreshold } from "../../../contexts/ThresholdContext"
30-
import { isAddressZero, isSameETHAddress } from "../../../web3/utils"
31-
import { useOperatorMappedtoStakingProviderHelpers } from "../../../hooks/staking-applications/useOperatorMappedToStakingProviderHelpers"
28+
import {
29+
isAddressZero,
30+
isSameETHAddress,
31+
AddressZero,
32+
} from "../../../web3/utils"
33+
34+
export interface MapOperatorToStakingProviderModalProps {
35+
mappedOperatorTbtc: string
36+
mappedOperatorRandomBeacon: string
37+
}
3238

33-
const MapOperatorToStakingProviderModal: FC<BaseModalProps> = () => {
39+
const MapOperatorToStakingProviderModal: FC<
40+
BaseModalProps & MapOperatorToStakingProviderModalProps
41+
> = ({ mappedOperatorTbtc, mappedOperatorRandomBeacon }) => {
3442
const { account } = useWeb3React()
35-
const operatorMappedToStakingProviderHelpers =
36-
useOperatorMappedtoStakingProviderHelpers()
37-
const {
38-
isOperatorMappedOnlyInRandomBeacon,
39-
isOperatorMappedOnlyInTbtc,
40-
operatorMappedRandomBeacon,
41-
operatorMappedTbtc,
42-
} = operatorMappedToStakingProviderHelpers
4343
const formRef =
4444
useRef<FormikProps<MapOperatorToStakingProviderFormValues>>(null)
4545
const { closeModal, openModal } = useModal()
4646
const threshold = useThreshold()
4747

48+
const isOperatorMappedOnlyInTbtc =
49+
!isAddressZero(mappedOperatorTbtc) &&
50+
isAddressZero(mappedOperatorRandomBeacon)
51+
52+
const isOperatorMappedOnlyInRandomBeacon =
53+
isAddressZero(mappedOperatorTbtc) &&
54+
!isAddressZero(mappedOperatorRandomBeacon)
55+
4856
const onSubmit = async ({
4957
operator,
5058
}: MapOperatorToStakingProviderFormValues) => {
5159
if (account) {
5260
openModal(ModalType.MapOperatorToStakingProviderConfirmation, {
5361
operator,
62+
mappedOperatorTbtc,
63+
mappedOperatorRandomBeacon,
5464
})
5565
}
5666
}
@@ -85,13 +95,14 @@ const MapOperatorToStakingProviderModal: FC<BaseModalProps> = () => {
8595
</H5>
8696
) : (
8797
<H5>
88-
We’ve noticed your wallet address is the same with your Provider
98+
We’ve noticed your wallet address is the same as your Provider
8999
Address
90100
</H5>
91101
)}
92102
<BodyLg mt="4">
93-
Would you like to map your Operator Address? Mapping an Operator
94-
Address will require one transaction per application.
103+
Map your Operator Address to your Provider Address to improve the
104+
support of your hardware wallet. Mapping will require one
105+
transaction per application.
95106
</BodyLg>
96107
</InfoBox>
97108
<BodyLg mt={"10"}>
@@ -118,18 +129,17 @@ const MapOperatorToStakingProviderModal: FC<BaseModalProps> = () => {
118129
formId="map-operator-to-staking-provider-form"
119130
initialAddress={
120131
isOperatorMappedOnlyInRandomBeacon
121-
? operatorMappedRandomBeacon
132+
? mappedOperatorRandomBeacon
122133
: isOperatorMappedOnlyInTbtc
123-
? operatorMappedTbtc
134+
? mappedOperatorTbtc
124135
: ""
125136
}
126137
onSubmitForm={onSubmit}
127138
checkIfOperatorIsMappedToAnotherStakingProvider={
128139
checkIfOperatorIsMappedToAnotherStakingProvider
129140
}
130-
operatorMappedToStakingProviderHelpers={
131-
operatorMappedToStakingProviderHelpers
132-
}
141+
mappedOperatorTbtc={mappedOperatorTbtc}
142+
mappedOperatorRandomBeacon={mappedOperatorRandomBeacon}
133143
/>
134144
</Box>
135145
<AlertBox

src/components/Modal/MapOperatorToStakingProviderSuccessModal/index.tsx

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
1-
import { FC } from "react"
2-
import {
3-
BodyMd,
4-
BodySm,
5-
HStack,
6-
List,
7-
ListItem,
8-
} from "@threshold-network/components"
1+
import { FC, Fragment } from "react"
2+
import { BodySm, HStack, List, ListItem } from "@threshold-network/components"
93
import withBaseModal from "../withBaseModal"
104
import { BaseModalProps } from "../../../types"
115
import { StakingAppName } from "../../../store/staking-applications"
126
import TransactionSuccessModal from "../TransactionSuccessModal"
137
import shortenAddress from "../../../utils/shortenAddress"
148
import { ExplorerDataType } from "../../../utils/createEtherscanLink"
159
import ViewInBlockExplorer from "../../ViewInBlockExplorer"
16-
import { camelCaseToNormal } from "../../../utils/text"
1710

1811
export type OperatorMappedSuccessTx = {
1912
application: {
@@ -36,44 +29,51 @@ const MapOperatorToStakingProviderSuccessBase: FC<
3629
subTitle="You successfully mapped your Operator Address."
3730
body={
3831
<>
39-
<List spacing="2" mb={"4rem"}>
40-
<ListItem key="map_operator_succes_modal__operator">
32+
<List spacing="2" mb={"16"}>
33+
<ListItem>
4134
<HStack justify="space-between">
42-
<BodySm>Operator</BodySm>
35+
<BodySm>Provider Address</BodySm>
4336
<BodySm>
44-
{shortenAddress(transactions[0].application.operator)}
37+
{shortenAddress(transactions[0].application.stakingProvider)}
4538
</BodySm>
4639
</HStack>
4740
</ListItem>
48-
<ListItem key="map_operator_succes_modal__staking_provider">
41+
<ListItem>
4942
<HStack justify="space-between">
50-
<BodySm>Staking Provider</BodySm>
43+
<BodySm>Operator Address</BodySm>
5144
<BodySm>
52-
{shortenAddress(transactions[0].application.stakingProvider)}
45+
{shortenAddress(transactions[0].application.operator)}
5346
</BodySm>
5447
</HStack>
5548
</ListItem>
5649
</List>
57-
{transactions.map((transaction, i) => {
58-
const text = `${camelCaseToNormal(
59-
transaction.application.appName
60-
)} transaction`
61-
return (
62-
<BodySm
63-
align="center"
64-
mt={1}
65-
key={`map_operator_transaction_${i}`}
66-
>
67-
View{" "}
50+
<BodySm align="center" mt={"1"}>
51+
{transactions.length === 1 ? (
52+
<>
6853
<ViewInBlockExplorer
69-
text={text}
70-
id={transaction.txHash}
54+
text="View"
55+
id={transactions[0].txHash}
7156
type={ExplorerDataType.TRANSACTION}
7257
/>{" "}
58+
transaction on Etherscan
59+
</>
60+
) : (
61+
<>
62+
View{" "}
63+
{transactions.map((_, index) => (
64+
<Fragment key={_.txHash}>
65+
<ViewInBlockExplorer
66+
text={`transaction ${index + 1}`}
67+
id={_.txHash}
68+
type={ExplorerDataType.TRANSACTION}
69+
/>
70+
{index + 1 === transactions.length ? " " : " and "}
71+
</Fragment>
72+
))}
7373
on Etherscan
74-
</BodySm>
75-
)
76-
})}
74+
</>
75+
)}
76+
</BodySm>
7777
</>
7878
}
7979
/>

src/hooks/staking-applications/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
export * from "./useOperatorsMappedToStakingProvider"
21
export * from "./useStakingAppContract"
32
export * from "./useStakingAppDataByStakingProvider"
43
export * from "./useStakingApplicationState"

0 commit comments

Comments
 (0)