From 42a0675bf2d3c14529556a98b0516f6cd1a00ba1 Mon Sep 17 00:00:00 2001
From: Rafal Czajkowski <rafal.czajkowski@keep.network>
Date: Wed, 9 Nov 2022 13:38:37 +0100
Subject: [PATCH 1/6] Move fetching owner stakes to redux

We want to fetch the owner stakes only once so the best place for
fetching this data is one-shot listener effect.
---
 src/hooks/useFetchOwnerStakes.ts              | 163 ------------------
 src/store/staking/effects.ts                  |  38 ++++
 src/store/staking/stakingSlice.ts             |  13 +-
 .../staking/__test__/staking.test.ts          |   4 +-
 src/threshold-ts/staking/index.ts             |   5 +-
 5 files changed, 53 insertions(+), 170 deletions(-)
 delete mode 100644 src/hooks/useFetchOwnerStakes.ts

diff --git a/src/hooks/useFetchOwnerStakes.ts b/src/hooks/useFetchOwnerStakes.ts
deleted file mode 100644
index ae463783c..000000000
--- a/src/hooks/useFetchOwnerStakes.ts
+++ /dev/null
@@ -1,163 +0,0 @@
-import { useCallback } from "react"
-import { BigNumber } from "@ethersproject/bignumber"
-import {
-  useTStakingContract,
-  T_STAKING_CONTRACT_DEPLOYMENT_BLOCK,
-  usePREContract,
-  useKeepTokenStakingContract,
-} from "../web3/hooks"
-import {
-  getContractPastEvents,
-  getAddress,
-  isSameETHAddress,
-  isAddress,
-} from "../web3/utils"
-import { StakeType, Token } from "../enums"
-import { StakeData } from "../types/staking"
-import { setStakes } from "../store/staking"
-import { useDispatch } from "react-redux"
-import { useFetchPreConfigData } from "./useFetchPreConfigData"
-import { useTConvertedAmount } from "./useTConvertedAmount"
-import { useNuStakingEscrowContract } from "../web3/hooks/useNuStakingEscrowContract"
-import { useThreshold } from "../contexts/ThresholdContext"
-
-export const useFetchOwnerStakes = () => {
-  const tStakingContract = useTStakingContract()
-
-  const keepStakingContract = useKeepTokenStakingContract()
-  const nuStakingEscrowContract = useNuStakingEscrowContract()
-
-  const simplePREApplicationContract = usePREContract()
-
-  const fetchPreConfigData = useFetchPreConfigData()
-
-  const { convertToT: convertKeepToT } = useTConvertedAmount(Token.Keep, "0")
-  const { convertToT: convertNuToT } = useTConvertedAmount(Token.Nu, "0")
-
-  const dispatch = useDispatch()
-  const threshold = useThreshold()
-
-  return useCallback(
-    async (address?: string): Promise<StakeData[]> => {
-      if (
-        !tStakingContract ||
-        !nuStakingEscrowContract ||
-        !keepStakingContract ||
-        !simplePREApplicationContract ||
-        !address
-      ) {
-        return []
-      }
-
-      const stakedEvents = (
-        await getContractPastEvents(tStakingContract, {
-          eventName: "Staked",
-          fromBlock: T_STAKING_CONTRACT_DEPLOYMENT_BLOCK,
-          filterParams: [undefined, address],
-        })
-      ).reverse()
-
-      const stakingProviderToBeneficiary = stakedEvents.reduce(
-        (reducer, event): { [stakingProvider: string]: string } => {
-          reducer[event.args?.stakingProvider as string] = event.args
-            ?.beneficiary as string
-          return reducer
-        },
-        {} as { [stakingProvider: string]: string }
-      )
-
-      const stakingProviders = Object.keys(stakingProviderToBeneficiary)
-
-      const preConfigData = await fetchPreConfigData(stakingProviders)
-
-      const eligibleKeepStakes = await threshold.multicall.aggregate(
-        stakingProviders.map((stakingProvider) => ({
-          interface: keepStakingContract.interface,
-          address: keepStakingContract.address,
-          method: "eligibleStake",
-          args: [stakingProvider, tStakingContract.address],
-        }))
-      )
-
-      // The NU staker can have only one stake.
-      const { stakingProvider: nuStakingProvider, value: nuStake } =
-        await nuStakingEscrowContract.stakerInfo(address)
-
-      const stakes = stakedEvents.map((_) => {
-        const amount = _.args?.amount.toString()
-        const stakeType = _.args?.stakeType as StakeType
-        const stakingProvider = getAddress(_.args?.stakingProvider as string)
-
-        return {
-          stakeType,
-          owner: _.args?.owner as string,
-          stakingProvider,
-          beneficiary: _.args?.beneficiary as string,
-          authorizer: _.args?.authorizer as string,
-          blockNumber: _.blockNumber,
-          blockHash: _.blockHash,
-          transactionHash: _.transactionHash,
-          nuInTStake: stakeType === StakeType.NU ? amount : "0",
-          keepInTStake: stakeType === StakeType.KEEP ? amount : "0",
-          tStake: stakeType === StakeType.T ? amount : "0",
-          preConfig: preConfigData[stakingProvider],
-        } as StakeData
-      })
-
-      const data = await threshold.multicall.aggregate(
-        stakes.map((_) => ({
-          interface: tStakingContract.interface,
-          address: tStakingContract.address,
-          method: "stakes",
-          args: [_.stakingProvider],
-        }))
-      )
-
-      data.forEach((_, index) => {
-        const total = BigNumber.from(_.tStake)
-          .add(BigNumber.from(_.keepInTStake))
-          .add(BigNumber.from(_.nuInTStake))
-        const keepInTStake = _.keepInTStake.toString()
-        const keepEligableStakeInT = convertKeepToT(
-          eligibleKeepStakes[index].toString()
-        )
-        const possibleKeepTopUpInT = BigNumber.from(keepEligableStakeInT)
-          .sub(BigNumber.from(keepInTStake))
-          .toString()
-
-        const stakingProvider = stakes[index].stakingProvider
-        const nuInTStake = stakes[index].nuInTStake.toString()
-
-        const possibleNuTopUpInT =
-          isAddress(nuStakingProvider) &&
-          isSameETHAddress(stakingProvider, nuStakingProvider)
-            ? BigNumber.from(convertNuToT(nuStake))
-                .sub(BigNumber.from(nuInTStake))
-                .toString()
-            : "0"
-
-        stakes[index] = {
-          ...stakes[index],
-          tStake: _.tStake.toString(),
-          keepInTStake,
-          nuInTStake,
-          totalInTStake: total.toString(),
-          possibleKeepTopUpInT,
-          possibleNuTopUpInT,
-        }
-      })
-
-      dispatch(setStakes(stakes))
-
-      return stakes
-    },
-    [
-      tStakingContract,
-      dispatch,
-      convertKeepToT,
-      convertNuToT,
-      fetchPreConfigData,
-      threshold,
-    ]
-  )
-}
diff --git a/src/store/staking/effects.ts b/src/store/staking/effects.ts
index 97dc954ef..0e98270b2 100644
--- a/src/store/staking/effects.ts
+++ b/src/store/staking/effects.ts
@@ -1,5 +1,7 @@
+import { Stake } from "../../threshold-ts/staking"
 import { StakeData } from "../../types"
 import { AddressZero, isAddress, isAddressZero } from "../../web3/utils"
+import { walletConnected } from "../account"
 import { AppListenerEffectAPI } from "../listener"
 import { selectStakeByStakingProvider } from "./selectors"
 import { requestStakeByStakingProvider, setStakes } from "./stakingSlice"
@@ -82,3 +84,39 @@ const fetchStake = async (
     ])
   )
 }
+
+export const fetchOwnerStakesEffect = async (
+  actionCreator: ReturnType<typeof walletConnected>,
+  listenerApi: AppListenerEffectAPI
+) => {
+  const address = actionCreator.payload
+  if (!isAddress(address)) return
+
+  listenerApi.unsubscribe()
+
+  try {
+    const stakes = await listenerApi.extra.threshold.staking.getOwnerStakes(
+      address
+    )
+
+    listenerApi.dispatch(
+      setStakes(
+        stakes.map(
+          (stake) =>
+            ({
+              ...stake,
+              tStake: stake.tStake.toString(),
+              nuInTStake: stake.tStake.toString(),
+              keepInTStake: stake.keepInTStake.toString(),
+              totalInTStake: stake.totalInTStake.toString(),
+              possibleKeepTopUpInT: stake.possibleKeepTopUpInT.toString(),
+              possibleNuTopUpInT: stake.possibleNuTopUpInT.toString(),
+            } as Stake<string>)
+        )
+      )
+    )
+  } catch (error) {
+    console.log("Could not fetch owner stakes", error)
+    listenerApi.subscribe()
+  }
+}
diff --git a/src/store/staking/stakingSlice.ts b/src/store/staking/stakingSlice.ts
index c2d7a006a..4923180e5 100644
--- a/src/store/staking/stakingSlice.ts
+++ b/src/store/staking/stakingSlice.ts
@@ -12,7 +12,11 @@ import { StakeType, TopUpType, UnstakeType } from "../../enums"
 import { AddressZero } from "../../web3/utils"
 import { UpdateStateActionPayload } from "../../types/state"
 import { startAppListening } from "../listener"
-import { fetchStakeByStakingProviderEffect } from "./effects"
+import { walletConnected } from "../account"
+import {
+  fetchStakeByStakingProviderEffect,
+  fetchOwnerStakesEffect,
+} from "./effects"
 
 interface StakingState {
   stakingProvider: string
@@ -187,10 +191,13 @@ export const {
 } = stakingSlice.actions
 
 export const registerStakingListeners = () => {
+  startAppListening({
+    actionCreator: walletConnected,
+    effect: fetchOwnerStakesEffect,
+  })
+
   startAppListening({
     actionCreator: requestStakeByStakingProvider,
     effect: fetchStakeByStakingProviderEffect,
   })
 }
-
-registerStakingListeners()
diff --git a/src/threshold-ts/staking/__test__/staking.test.ts b/src/threshold-ts/staking/__test__/staking.test.ts
index 310410b2c..b46f69dac 100644
--- a/src/threshold-ts/staking/__test__/staking.test.ts
+++ b/src/threshold-ts/staking/__test__/staking.test.ts
@@ -218,10 +218,10 @@ describe("Staking test", () => {
     ])
 
     expect(vendingMachines.keep.convertToT).toHaveBeenCalledWith(
-      eligibleKeepStake
+      eligibleKeepStake.toString()
     )
     expect(vendingMachines.nu.convertToT).toHaveBeenCalledWith(
-      nuStakerInfo.value
+      nuStakerInfo.value.toString()
     )
 
     expect(result).toEqual({
diff --git a/src/threshold-ts/staking/index.ts b/src/threshold-ts/staking/index.ts
index 5c3d5c640..709487fcd 100644
--- a/src/threshold-ts/staking/index.ts
+++ b/src/threshold-ts/staking/index.ts
@@ -199,12 +199,13 @@ export class Staking implements IStaking {
       isAddress(nuStakingProvider) &&
       isSameETHAddress(stakingProvider, nuStakingProvider)
         ? BigNumber.from(
-            (await this._vendingMachines.nu.convertToT(nuStake)).tAmount
+            (await this._vendingMachines.nu.convertToT(nuStake.toString()))
+              .tAmount
           ).sub(BigNumber.from(nuInTStake))
         : ZERO
 
     const keepEligableStakeInT = (
-      await this._vendingMachines.keep.convertToT(eligibleKeepStake)
+      await this._vendingMachines.keep.convertToT(eligibleKeepStake.toString())
     ).tAmount
     const possibleKeepTopUpInT = BigNumber.from(keepEligableStakeInT).sub(
       BigNumber.from(keepInTStake)

From fced6c386ffb59e81b2afb53159f8bdfbbf9b8a5 Mon Sep 17 00:00:00 2001
From: Rafal Czajkowski <rafal.czajkowski@keep.network>
Date: Wed, 9 Nov 2022 13:42:19 +0100
Subject: [PATCH 2/6] Refactor registering listeners

We should register listener in different file than the one in which it
is defined otherwise we may run into an issue with webpack import
orddering. Here we create a common function that should register all
listeners and we call this fn after creating a store and after resetting
the store.
---
 src/store/account/slice.ts              |  1 -
 src/store/index.ts                      | 20 ++++++++------------
 src/store/listener.ts                   | 11 +++++++++++
 src/store/staking-applications/slice.ts |  1 -
 src/store/tokens/tokenSlice.ts          |  1 -
 5 files changed, 19 insertions(+), 15 deletions(-)

diff --git a/src/store/account/slice.ts b/src/store/account/slice.ts
index 570f397f8..75a7c4252 100644
--- a/src/store/account/slice.ts
+++ b/src/store/account/slice.ts
@@ -103,7 +103,6 @@ export const registerAccountListeners = () => {
     })
   }
 }
-registerAccountListeners()
 
 export const {
   walletConnected,
diff --git a/src/store/index.ts b/src/store/index.ts
index 242ea4aec..cf2175dcd 100644
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -5,19 +5,16 @@ import {
   Reducer,
 } from "@reduxjs/toolkit"
 import { modalSlice } from "./modal"
-import { registerTokensListeners, tokenSlice } from "./tokens"
+import { tokenSlice } from "./tokens"
 import { sidebarSlice } from "./sidebar"
 import { transactionSlice } from "./transactions"
-import { registerStakingListeners, stakingSlice } from "./staking"
+import { stakingSlice } from "./staking"
 import { ethSlice } from "./eth"
 import { rewardsSlice } from "./rewards"
 import { tbtcSlice } from "./tbtc"
-import {
-  registerStakingAppsListeners,
-  stakingApplicationsSlice,
-} from "./staking-applications/slice"
-import { listenerMiddleware } from "./listener"
-import { accountSlice, registerAccountListeners } from "./account"
+import { stakingApplicationsSlice } from "./staking-applications/slice"
+import { listenerMiddleware, registerListeners } from "./listener"
+import { accountSlice } from "./account"
 
 const combinedReducer = combineReducers({
   account: accountSlice.reducer,
@@ -41,10 +38,7 @@ export const resetStoreAction = () => ({
 const rootReducer: Reducer = (state: RootState, action: AnyAction) => {
   if (action.type === APP_RESET_STORE) {
     listenerMiddleware.clearListeners()
-    registerStakingListeners()
-    registerStakingAppsListeners()
-    registerAccountListeners()
-    registerTokensListeners()
+    registerListeners()
     state = {
       eth: { ...state.eth },
       token: {
@@ -93,6 +87,8 @@ const store = configureStore({
     }).prepend(listenerMiddleware.middleware),
 })
 
+registerListeners()
+
 export type RootState = ReturnType<
   typeof store.getState & typeof combinedReducer
 >
diff --git a/src/store/listener.ts b/src/store/listener.ts
index f4f65b845..f760f19bf 100644
--- a/src/store/listener.ts
+++ b/src/store/listener.ts
@@ -6,6 +6,10 @@ import {
 import { AppDispatch, RootState } from "."
 import { Threshold } from "../threshold-ts"
 import { threshold } from "../utils/getThresholdLib"
+import { registerTokensListeners } from "./tokens"
+import { registerStakingListeners } from "./staking"
+import { registerStakingAppsListeners } from "./staking-applications/slice"
+import { registerAccountListeners } from "./account"
 
 export const listenerMiddleware = createListenerMiddleware({
   extra: { threshold },
@@ -29,3 +33,10 @@ export type AppListenerEffectAPI = ListenerEffectAPI<
 
 export const startAppListening =
   listenerMiddleware.startListening as AppStartListening
+
+export const registerListeners = () => {
+  registerAccountListeners()
+  registerTokensListeners()
+  registerStakingListeners()
+  registerStakingAppsListeners()
+}
diff --git a/src/store/staking-applications/slice.ts b/src/store/staking-applications/slice.ts
index 4fdeb67d1..bcc030374 100644
--- a/src/store/staking-applications/slice.ts
+++ b/src/store/staking-applications/slice.ts
@@ -282,4 +282,3 @@ export const registerStakingAppsListeners = () => {
     })
   }
 }
-registerStakingAppsListeners()
diff --git a/src/store/tokens/tokenSlice.ts b/src/store/tokens/tokenSlice.ts
index 7f1f5a515..847d74d60 100644
--- a/src/store/tokens/tokenSlice.ts
+++ b/src/store/tokens/tokenSlice.ts
@@ -114,4 +114,3 @@ export const registerTokensListeners = () => {
     effect: fetchTokenBalances,
   })
 }
-registerTokensListeners()

From 17d6e1ba752e3b5ee3ee563592662e8fc7fe120e Mon Sep 17 00:00:00 2001
From: Rafal Czajkowski <rafal.czajkowski@keep.network>
Date: Wed, 9 Nov 2022 14:41:17 +0100
Subject: [PATCH 3/6] Clean up the `StakeData` type

Use the `Stake` type from the threshold ts lib- this removes the
`preConfig` filed from the `StakeData` type. The PRE is a staking app so
should be stored in a dedicated slice(rewards). We are going to add the
PRE app to the rewards slice in a follow-up work.
---
 .../Modal/TopupTModal/LegacyTopUpModal.tsx    |  2 +-
 src/hooks/useSubscribeToStakedEvent.ts        | 17 ++------------
 src/pages/Staking/StakeCard/index.tsx         |  6 ++---
 src/store/staking/stakingSlice.ts             |  6 -----
 src/types/staking.ts                          | 22 +++----------------
 5 files changed, 9 insertions(+), 44 deletions(-)

diff --git a/src/components/Modal/TopupTModal/LegacyTopUpModal.tsx b/src/components/Modal/TopupTModal/LegacyTopUpModal.tsx
index 44801372b..f836ceeb0 100644
--- a/src/components/Modal/TopupTModal/LegacyTopUpModal.tsx
+++ b/src/components/Modal/TopupTModal/LegacyTopUpModal.tsx
@@ -108,7 +108,7 @@ const LegacyTopUpModal: FC<BaseModalProps & { stake: StakeData }> = ({
                 <Button
                   as={Link}
                   isExternal
-                  href={stakeTypeToDappHref[stake.stakeType]}
+                  href={stakeTypeToDappHref[stake.stakeType ?? StakeType.KEEP]}
                   isFullWidth
                   mb="3"
                 >
diff --git a/src/hooks/useSubscribeToStakedEvent.ts b/src/hooks/useSubscribeToStakedEvent.ts
index 4aee43123..96d708e59 100644
--- a/src/hooks/useSubscribeToStakedEvent.ts
+++ b/src/hooks/useSubscribeToStakedEvent.ts
@@ -7,7 +7,6 @@ import {
   providerStakedForStakingProvider,
 } from "../store/staking"
 import { useSubscribeToContractEvent, useTStakingContract } from "../web3/hooks"
-import { isAddress, isSameETHAddress } from "../web3/utils"
 
 export const useSubscribeToStakedEvent = () => {
   const tStakingContract = useTStakingContract()
@@ -31,11 +30,8 @@ export const useSubscribeToStakedEvent = () => {
       stakingProvider: string,
       beneficiary: string,
       authorizer: string,
-      amount: BigNumberish,
-      event: Event
+      amount: BigNumberish
     ) => {
-      // TODO: open success modal here
-      const { blockNumber, blockHash, transactionHash } = event
       dispatch(
         providerStaked({
           stakeType,
@@ -43,9 +39,6 @@ export const useSubscribeToStakedEvent = () => {
           stakingProvider,
           authorizer,
           beneficiary,
-          blockHash,
-          blockNumber,
-          transactionHash,
           amount: amount.toString(),
         })
       )
@@ -70,11 +63,8 @@ export const useSubscribeToStakedEvent = () => {
       stakingProvider: string,
       beneficiary: string,
       authorizer: string,
-      amount: BigNumberish,
-      event: Event
+      amount: BigNumberish
     ) => {
-      // TODO: open success modal here
-      const { blockNumber, blockHash, transactionHash } = event
       dispatch(
         providerStakedForStakingProvider({
           stakeType,
@@ -82,9 +72,6 @@ export const useSubscribeToStakedEvent = () => {
           stakingProvider,
           authorizer,
           beneficiary,
-          blockHash,
-          blockNumber,
-          transactionHash,
           amount: amount.toString(),
         })
       )
diff --git a/src/pages/Staking/StakeCard/index.tsx b/src/pages/Staking/StakeCard/index.tsx
index 729a59880..2dacb3ba2 100644
--- a/src/pages/Staking/StakeCard/index.tsx
+++ b/src/pages/Staking/StakeCard/index.tsx
@@ -41,9 +41,9 @@ const StakeCardProvider: FC<{ stake: StakeData }> = ({ stake }) => {
   const canTopUpKepp = BigNumber.from(stake.possibleKeepTopUpInT).gt(0)
   const canTopUpNu = BigNumber.from(stake.possibleNuTopUpInT).gt(0)
   const hasLegacyStakes = stake.nuInTStake !== "0" || stake.keepInTStake !== "0"
-  const isPRESet =
-    !isAddressZero(stake.preConfig.operator) &&
-    stake.preConfig.isOperatorConfirmed
+  const isPRESet = true
+  // !isAddressZero(stake.preConfig.operator) &&
+  // stake.preConfig.isOperatorConfirmed
 
   return (
     <StakeCardContext.Provider
diff --git a/src/store/staking/stakingSlice.ts b/src/store/staking/stakingSlice.ts
index 4923180e5..e17af29bc 100644
--- a/src/store/staking/stakingSlice.ts
+++ b/src/store/staking/stakingSlice.ts
@@ -75,12 +75,6 @@ export const stakingSlice = createSlice({
       newStake.possibleKeepTopUpInT = "0"
       newStake.possibleNuTopUpInT = "0"
 
-      newStake.preConfig = {
-        operator: AddressZero,
-        isOperatorConfirmed: false,
-        operatorStartTimestamp: "0",
-      }
-
       state.stakes = [newStake, ...state.stakes]
       state.stakedBalance = calculateStakedBalance(state.stakes)
     },
diff --git a/src/types/staking.ts b/src/types/staking.ts
index 86fc84606..7bee481ea 100644
--- a/src/types/staking.ts
+++ b/src/types/staking.ts
@@ -1,5 +1,6 @@
 import { BigNumberish } from "@ethersproject/bignumber"
-import { StakeType, TopUpType, UnstakeType } from "../enums"
+import { TopUpType, UnstakeType } from "../enums"
+import { Stake } from "../threshold-ts/staking"
 import { UpdateStateActionPayload } from "./state"
 
 export type StakingStateKey =
@@ -35,23 +36,7 @@ export interface PreConfigData {
   [stakingProvider: string]: PreConfig
 }
 
-export interface StakeData {
-  stakeType: StakeType
-  owner: string
-  stakingProvider: string
-  beneficiary: string
-  authorizer: string
-  blockNumber: number
-  blockHash: string
-  transactionHash: string
-  nuInTStake: string
-  keepInTStake: string
-  tStake: string
-  totalInTStake: string
-  preConfig: PreConfig
-  possibleKeepTopUpInT: string
-  possibleNuTopUpInT: string
-}
+export type StakeData = Stake<string>
 
 export interface ProviderStakedEvent {
   stakeType: number
@@ -71,7 +56,6 @@ export type ProviderStakedActionPayload = ProviderStakedEvent &
     | "tStake"
     | "amount"
     | "totalInTStake"
-    | "preConfig"
     | "possibleKeepTopUpInT"
     | "possibleNuTopUpInT"
   >

From 527d535e37c32edda6586125e848d87e4caf44c1 Mon Sep 17 00:00:00 2001
From: Rafal Czajkowski <rafal.czajkowski@keep.network>
Date: Thu, 10 Nov 2022 14:53:31 +0100
Subject: [PATCH 4/6] Move fetching the PRE app data to ts-lib and redux

The PRE is a seprate app so should be stored in the `staking-apps` slice
not as a part of the stake data.
---
 src/hooks/useFetchPreConfigData.ts            |  43 -------
 src/pages/Staking/StakeCard/index.tsx         |  10 +-
 src/store/staking-applications/effects.ts     | 112 ++++++++++++++----
 src/store/staking-applications/selectors.ts   |  10 ++
 src/store/staking-applications/slice.ts       |  39 ++++--
 .../applications/pre/abi.json}                |   0
 src/threshold-ts/applications/pre/index.ts    | 101 ++++++++++++++++
 src/threshold-ts/mas/index.ts                 |   3 +
 src/web3/hooks/usePREContract.ts              |  24 +---
 9 files changed, 242 insertions(+), 100 deletions(-)
 delete mode 100644 src/hooks/useFetchPreConfigData.ts
 rename src/{web3/abi/SimplePreApplication.json => threshold-ts/applications/pre/abi.json} (100%)
 create mode 100644 src/threshold-ts/applications/pre/index.ts

diff --git a/src/hooks/useFetchPreConfigData.ts b/src/hooks/useFetchPreConfigData.ts
deleted file mode 100644
index dd0fffdc9..000000000
--- a/src/hooks/useFetchPreConfigData.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { useCallback } from "react"
-import { usePREContract } from "../web3/hooks"
-import { PreConfigData } from "../types/staking"
-import { useThreshold } from "../contexts/ThresholdContext"
-
-export const useFetchPreConfigData = (): ((
-  stakingProviders: string[]
-) => Promise<PreConfigData>) => {
-  const preContract = usePREContract()
-  const threshold = useThreshold()
-
-  return useCallback(
-    async (stakingProviders) => {
-      if (!stakingProviders || stakingProviders.length === 0 || !preContract) {
-        return {} as PreConfigData
-      }
-
-      const preConfigDataRaw = await threshold.multicall.aggregate(
-        stakingProviders.map((stakingProvider) => {
-          return {
-            interface: preContract.interface,
-            address: preContract.address,
-            method: "stakingProviderInfo",
-            args: [stakingProvider],
-          }
-        })
-      )
-
-      return preConfigDataRaw.reduce(
-        (finalData: PreConfigData, _, idx): PreConfigData => {
-          finalData[stakingProviders[idx]] = {
-            operator: _.operator,
-            isOperatorConfirmed: _.operatorConfirmed,
-            operatorStartTimestamp: _.operatorStartTimestamp.toString(),
-          }
-          return finalData
-        },
-        {}
-      )
-    },
-    [preContract, threshold]
-  )
-}
diff --git a/src/pages/Staking/StakeCard/index.tsx b/src/pages/Staking/StakeCard/index.tsx
index 2dacb3ba2..10a08e766 100644
--- a/src/pages/Staking/StakeCard/index.tsx
+++ b/src/pages/Staking/StakeCard/index.tsx
@@ -24,7 +24,7 @@ import {
   TopUpType,
   UnstakeType,
 } from "../../../enums"
-import { AddressZero, isAddressZero } from "../../../web3/utils"
+import { AddressZero } from "../../../web3/utils"
 import StakeApplications from "./StakeApplications"
 import StakeCardHeader from "./Header"
 import StakeRewards from "./StakeRewards"
@@ -35,15 +35,17 @@ import { StakeCardContext } from "../../../contexts/StakeCardContext"
 import { useStakeCardContext } from "../../../hooks/useStakeCardContext"
 import { isSameETHAddress } from "../../../threshold-ts/utils"
 import { useWeb3React } from "@web3-react/core"
+import { useAppSelector } from "../../../hooks/store"
+import { selectPREAppDataByStakingProvider } from "../../../store/staking-applications"
 
 const StakeCardProvider: FC<{ stake: StakeData }> = ({ stake }) => {
   const isInactiveStake = BigNumber.from(stake.totalInTStake).isZero()
   const canTopUpKepp = BigNumber.from(stake.possibleKeepTopUpInT).gt(0)
   const canTopUpNu = BigNumber.from(stake.possibleNuTopUpInT).gt(0)
   const hasLegacyStakes = stake.nuInTStake !== "0" || stake.keepInTStake !== "0"
-  const isPRESet = true
-  // !isAddressZero(stake.preConfig.operator) &&
-  // stake.preConfig.isOperatorConfirmed
+  const { isOperatorMapped: isPRESet } = useAppSelector((state) =>
+    selectPREAppDataByStakingProvider(state, stake.stakingProvider)
+  )
 
   return (
     <StakeCardContext.Provider
diff --git a/src/store/staking-applications/effects.ts b/src/store/staking-applications/effects.ts
index 86ba37261..cd1907184 100644
--- a/src/store/staking-applications/effects.ts
+++ b/src/store/staking-applications/effects.ts
@@ -1,5 +1,10 @@
 import { AnyAction } from "@reduxjs/toolkit"
-import { stakingApplicationsSlice, StakingAppName } from "./slice"
+import {
+  AllStakinApps,
+  AuthorizationNotRequiredApps,
+  stakingApplicationsSlice,
+  StakingAppName,
+} from "./slice"
 import { AppListenerEffectAPI } from "../listener"
 import {
   selectStakeByStakingProvider,
@@ -22,6 +27,11 @@ import {
   selectStakingAppStateByAppName,
 } from "./selectors"
 import { isAddressZero } from "../../web3/utils"
+import {
+  IPRE,
+  StakingProviderInfo as PREStakingProviderInfo,
+} from "../../threshold-ts/applications/pre"
+import { BigNumber, BigNumberish } from "ethers"
 
 export const getSupportedAppsEffect = async (
   action: ReturnType<typeof stakingApplicationsSlice.actions.getSupportedApps>,
@@ -115,17 +125,26 @@ export const getSupportedAppsStakingProvidersData = async (
       "randomBeacon",
       listenerApi
     )
+
+    await getPREAppStakingProvidersData(
+      stakingProviders,
+      listenerApi.extra.threshold.multiAppStaking.pre,
+      "pre",
+      listenerApi
+    )
   } catch (error) {
     console.log("Could not fetch apps data for staking providers ", error)
     listenerApi.subscribe()
   }
 }
 
-const getKeepStakingAppStakingProvidersData = async (
+const getStakingAppStakingProivdersData = async <DataType, MappedDataType>(
   stakingProviders: string[],
-  application: IApplication,
-  appName: StakingAppName,
-  listenerApi: AppListenerEffectAPI
+  application: IPRE | IApplication,
+  appName: AllStakinApps,
+  listenerApi: AppListenerEffectAPI,
+  mapResultTo: (data: DataType) => MappedDataType,
+  dispatchFn: (data: { [stakingProvider: string]: MappedDataType }) => AnyAction
 ) => {
   try {
     listenerApi.dispatch(
@@ -133,32 +152,27 @@ const getKeepStakingAppStakingProvidersData = async (
         appName,
       })
     )
-    const appData = await Promise.all(
-      stakingProviders.map(application.getStakingProviderAppInfo)
+
+    const appData: DataType[] = await Promise.all(
+      stakingProviders.map(
+        (stakingProvider) =>
+          application.getStakingProviderAppInfo(
+            stakingProvider
+          ) as unknown as Promise<DataType>
+      )
     )
+
     const appDataByStakingProvider = stakingProviders.reduce(
       (reducer, stakingProvider, index) => {
         const _appData = appData[index]
-        reducer[stakingProvider] = {
-          authorizedStake: _appData.authorizedStake.toString(),
-          pendingAuthorizationDecrease:
-            _appData.pendingAuthorizationDecrease.toString(),
-          remainingAuthorizationDecreaseDelay:
-            _appData.remainingAuthorizationDecreaseDelay.toString(),
-          isDeauthorizationReqestActive: _appData.isDeauthorizationReqestActive,
-          deauthorizationCreatedAt:
-            _appData.deauthorizationCreatedAt?.toString(),
-        }
+        reducer[stakingProvider] = mapResultTo(_appData)
+
         return reducer
       },
-      {} as { [stakingProvider: string]: StakingProviderAppInfo<string> }
-    )
-    listenerApi.dispatch(
-      stakingApplicationsSlice.actions.setStakingProvidersAppData({
-        appName,
-        data: appDataByStakingProvider,
-      })
+      {} as { [stakingProvider: string]: MappedDataType }
     )
+
+    listenerApi.dispatch(dispatchFn(appDataByStakingProvider))
   } catch (error) {
     listenerApi.dispatch(
       stakingApplicationsSlice.actions.setStakingProvidersAppDataError({
@@ -170,6 +184,56 @@ const getKeepStakingAppStakingProvidersData = async (
   }
 }
 
+const getKeepStakingAppStakingProvidersData = async (
+  stakingProviders: string[],
+  application: IApplication,
+  appName: StakingAppName,
+  listenerApi: AppListenerEffectAPI
+) => {
+  type MappedDataType = StakingProviderAppInfo<string>
+  const mapToResult = (data: StakingProviderAppInfo): MappedDataType => {
+    return {
+      authorizedStake: data.authorizedStake.toString(),
+      pendingAuthorizationDecrease:
+        data.pendingAuthorizationDecrease.toString(),
+      remainingAuthorizationDecreaseDelay:
+        data.remainingAuthorizationDecreaseDelay.toString(),
+      isDeauthorizationReqestActive: data.isDeauthorizationReqestActive,
+      deauthorizationCreatedAt: data.deauthorizationCreatedAt?.toString(),
+    }
+  }
+
+  await getStakingAppStakingProivdersData<
+    StakingProviderAppInfo,
+    MappedDataType
+  >(stakingProviders, application, appName, listenerApi, mapToResult, (data) =>
+    stakingApplicationsSlice.actions.setStakingProvidersAppData({
+      appName,
+      data,
+    })
+  )
+}
+
+const getPREAppStakingProvidersData = async (
+  stakingProviders: string[],
+  application: IPRE,
+  appName: AuthorizationNotRequiredApps,
+  listenerApi: AppListenerEffectAPI
+) => {
+  type MappedDataType = PREStakingProviderInfo
+  const mapToResult = (data: PREStakingProviderInfo): MappedDataType => data
+
+  await getStakingAppStakingProivdersData<
+    PREStakingProviderInfo,
+    MappedDataType
+  >(stakingProviders, application, appName, listenerApi, mapToResult, (data) =>
+    stakingApplicationsSlice.actions.setStakingProvidersAppData({
+      appName,
+      data,
+    })
+  )
+}
+
 export const displayMapOperatorToStakingProviderModalEffect = async (
   action: AnyAction,
   listenerApi: AppListenerEffectAPI
diff --git a/src/store/staking-applications/selectors.ts b/src/store/staking-applications/selectors.ts
index 45d4e2041..f60d0bd1f 100644
--- a/src/store/staking-applications/selectors.ts
+++ b/src/store/staking-applications/selectors.ts
@@ -52,3 +52,13 @@ export const selectStakingAppByStakingProvider = createSelector(
     }
   }
 )
+
+export const selectPREAppDataByStakingProvider = createSelector(
+  [
+    selectStakingAppState,
+    (_: RootState, stakingProvider: string) => stakingProvider,
+  ],
+  (applicationState: StakingApplicationsState, stakingProvider: string) => {
+    return applicationState.pre.stakingProviders.data[stakingProvider] ?? {}
+  }
+)
diff --git a/src/store/staking-applications/slice.ts b/src/store/staking-applications/slice.ts
index bcc030374..946b7ea0a 100644
--- a/src/store/staking-applications/slice.ts
+++ b/src/store/staking-applications/slice.ts
@@ -5,6 +5,7 @@ import {
   StakingProviderAppInfo,
   AuthorizationParameters,
 } from "../../threshold-ts/applications"
+import { StakingProviderInfo as PREStakingProviderInfo } from "../../threshold-ts/applications/pre"
 import { MAX_UINT64 } from "../../threshold-ts/utils"
 import { FetchingState } from "../../types"
 import { startAppListening } from "../listener"
@@ -24,6 +25,17 @@ type StakingApplicationDataByStakingProvider = {
   [stakingProvider: string]: StakingProviderAppInfo<string>
 }
 
+type PREApplicationDataByStakingProvider = {
+  [stakingProvider: string]: PREStakingProviderInfo
+}
+
+type SetStakingProvidersAppDataPayload = PayloadAction<{
+  appName: AllStakinApps
+  data:
+    | StakingApplicationDataByStakingProvider
+    | PREApplicationDataByStakingProvider
+}>
+
 export type StakingApplicationState = {
   parameters: FetchingState<AuthorizationParameters<string>>
   stakingProviders: FetchingState<StakingApplicationDataByStakingProvider>
@@ -32,9 +44,12 @@ export type StakingApplicationState = {
 export interface StakingApplicationsState {
   tbtc: StakingApplicationState
   randomBeacon: StakingApplicationState
+  pre: { stakingProviders: FetchingState<PREApplicationDataByStakingProvider> }
 }
 
 export type StakingAppName = "tbtc" | "randomBeacon"
+export type AuthorizationNotRequiredApps = "pre"
+export type AllStakinApps = StakingAppName | AuthorizationNotRequiredApps
 
 export const stakingApplicationsSlice = createSlice({
   name: "staking-applications",
@@ -71,6 +86,13 @@ export const stakingApplicationsSlice = createSlice({
         data: {},
       },
     },
+    pre: {
+      stakingProviders: {
+        isFetching: false,
+        error: "",
+        data: {},
+      },
+    },
   } as StakingApplicationsState,
   reducers: {
     getSupportedApps: (state: StakingApplicationsState, action) => {},
@@ -105,12 +127,15 @@ export const stakingApplicationsSlice = createSlice({
     },
     setStakingProvidersAppData: (
       state: StakingApplicationsState,
-      action: PayloadAction<{
-        appName: StakingAppName
-        data: StakingApplicationDataByStakingProvider
-      }>
+      action: SetStakingProvidersAppDataPayload
     ) => {
-      const { appName, data } = action.payload
+      const { appName, data } = action.payload as {
+        appName: AllStakinApps
+        data: typeof action.payload.appName extends AuthorizationNotRequiredApps
+          ? PREApplicationDataByStakingProvider
+          : StakingApplicationDataByStakingProvider
+      }
+
       state[appName].stakingProviders = {
         isFetching: false,
         error: "",
@@ -120,7 +145,7 @@ export const stakingApplicationsSlice = createSlice({
     fetchingStakingProvidersAppData: (
       state: StakingApplicationsState,
       action: PayloadAction<{
-        appName: StakingAppName
+        appName: AllStakinApps
       }>
     ) => {
       const { appName } = action.payload
@@ -129,7 +154,7 @@ export const stakingApplicationsSlice = createSlice({
     setStakingProvidersAppDataError: (
       state: StakingApplicationsState,
       action: PayloadAction<{
-        appName: StakingAppName
+        appName: AllStakinApps
         error: string
       }>
     ) => {
diff --git a/src/web3/abi/SimplePreApplication.json b/src/threshold-ts/applications/pre/abi.json
similarity index 100%
rename from src/web3/abi/SimplePreApplication.json
rename to src/threshold-ts/applications/pre/abi.json
diff --git a/src/threshold-ts/applications/pre/index.ts b/src/threshold-ts/applications/pre/index.ts
new file mode 100644
index 000000000..90c2a72d2
--- /dev/null
+++ b/src/threshold-ts/applications/pre/index.ts
@@ -0,0 +1,101 @@
+import { BigNumber, Contract } from "ethers"
+import SimplePREApplicationABI from "./abi.json"
+import { AddressZero, isAddressZero, getContract } from "../../utils"
+import { EthereumConfig } from "../../types"
+
+const PRE_ADDRESSESS = {
+  // https://etherscan.io/address/0x7E01c9c03FD3737294dbD7630a34845B0F70E5Dd
+  1: "0x7E01c9c03FD3737294dbD7630a34845B0F70E5Dd",
+  // https://goerli.etherscan.io/address/0x829fdCDf6Be747FEA37518fBd83dF70EE371fCf2
+  // As NuCypher hasn't depoyed the `SimplePreApplication` contract on Goerli,
+  // we're using a stub contract.
+  5: "0x829fdCDf6Be747FEA37518fBd83dF70EE371fCf2",
+  // Set the correct `SimplePREApplication` contract address. If you deployed
+  // the `@threshold-network/solidity-contracts` to your local chain and linked
+  // package using `yarn link @threshold-network/solidity-contracts` you can
+  // find the contract address at
+  // `node_modules/@threshold-network/solidity-contracts/artifacts/SimplePREApplication.json`.
+  1337: AddressZero,
+} as Record<string, string>
+
+export interface StakingProviderInfo {
+  /**
+   * Operator address mapped to a given staking provider/
+   */
+  operator: string
+  /**
+   * Determines if the operator is confirmed.
+   */
+  isOperatorConfirmed: boolean
+  /**
+   * Timestamp where operator were bonded.
+   */
+  operatorStartTimestamp: string
+  /**
+   * Determines if the operator for the given staking provider is
+   * mapped.
+   */
+  isOperatorMapped: boolean
+}
+
+// NOTE: The simple PRE application contract doesn't implement the application
+// interface so we can't use the same interface as for the Keep staking apps.
+export interface IPRE {
+  /**
+   * Application address.
+   */
+  address: string
+
+  /**
+   * Application contract.
+   */
+  contract: Contract
+
+  getStakingProviderAppInfo: (
+    stakingProvider: string
+  ) => Promise<StakingProviderInfo>
+}
+
+export class PRE implements IPRE {
+  private _application: Contract
+
+  constructor(config: EthereumConfig) {
+    const address = PRE_ADDRESSESS[config.chainId]
+    if (!address) {
+      throw new Error("Unsupported chain id")
+    }
+
+    this._application = getContract(
+      address,
+      SimplePREApplicationABI,
+      config.providerOrSigner,
+      config.account
+    )
+  }
+  getStakingProviderAppInfo = async (
+    stakingProvider: string
+  ): Promise<StakingProviderInfo> => {
+    const operatorInfo = (await this._application.stakingProviderInfo(
+      stakingProvider
+    )) as {
+      operator: string
+      operatorConfirmed: boolean
+      operatorStartTimestamp: BigNumber
+    }
+
+    return {
+      operator: operatorInfo.operator,
+      isOperatorConfirmed: operatorInfo.operatorConfirmed,
+      operatorStartTimestamp: operatorInfo.operatorStartTimestamp.toString(),
+      isOperatorMapped:
+        !isAddressZero(operatorInfo.operator) && operatorInfo.operatorConfirmed,
+    }
+  }
+
+  get address() {
+    return this._application.address
+  }
+  get contract() {
+    return this._application
+  }
+}
diff --git a/src/threshold-ts/mas/index.ts b/src/threshold-ts/mas/index.ts
index f63a84e9c..aa30de8eb 100644
--- a/src/threshold-ts/mas/index.ts
+++ b/src/threshold-ts/mas/index.ts
@@ -8,6 +8,7 @@ import {
 import { IMulticall, ContractCall } from "../multicall"
 import { IStaking } from "../staking"
 import { EthereumConfig } from "../types"
+import { IPRE, PRE } from "../applications/pre"
 
 export interface SupportedAppAuthorizationParameters {
   tbtc: AuthorizationParameters
@@ -24,6 +25,7 @@ export class MultiAppStaking {
   private _multicall: IMulticall
   public readonly randomBeacon: IApplication
   public readonly ecdsa: IApplication
+  public readonly pre: IPRE
 
   constructor(
     staking: IStaking,
@@ -42,6 +44,7 @@ export class MultiAppStaking {
       abi: WalletRegistry.abi,
       ...config,
     })
+    this.pre = new PRE(config)
   }
 
   async getSupportedAppsAuthParameters(): Promise<SupportedAppAuthorizationParameters> {
diff --git a/src/web3/hooks/usePREContract.ts b/src/web3/hooks/usePREContract.ts
index 16de6d4c0..71682be75 100644
--- a/src/web3/hooks/usePREContract.ts
+++ b/src/web3/hooks/usePREContract.ts
@@ -1,28 +1,8 @@
-import SimplePREApplicationABI from "../abi/SimplePreApplication.json"
-import { useContract } from "./useContract"
+import { useThreshold } from "../../contexts/ThresholdContext"
 import { supportedChainId } from "../../utils/getEnvVariable"
-import { ChainID } from "../../enums"
-import { AddressZero } from "../utils"
 
 export const PRE_DEPLOYMENT_BLOCK = supportedChainId === "1" ? 14141140 : 0
 
-const PRE_ADDRESSESS = {
-  // https://etherscan.io/address/0x7E01c9c03FD3737294dbD7630a34845B0F70E5Dd
-  [ChainID.Ethereum.valueOf().toString()]:
-    "0x7E01c9c03FD3737294dbD7630a34845B0F70E5Dd",
-  // https://goerli.etherscan.io/address/0x829fdCDf6Be747FEA37518fBd83dF70EE371fCf2
-  // As NuCypher hasn't depoyed the `SimplePreApplication` contract on Goerli,
-  // we're using a stub contract.
-  [ChainID.Goerli.valueOf().toString()]:
-    "0x829fdCDf6Be747FEA37518fBd83dF70EE371fCf2",
-  // Set the correct `SimplePREApplication` contract address. If you deployed
-  // the `@threshold-network/solidity-contracts` to your local chain and linked
-  // package using `yarn link @threshold-network/solidity-contracts` you can
-  // find the contract address at
-  // `node_modules/@threshold-network/solidity-contracts/artifacts/SimplePREApplication.json`.
-  [ChainID.Localhost.valueOf().toString()]: AddressZero,
-} as Record<string, string>
-
 export const usePREContract = () => {
-  return useContract(PRE_ADDRESSESS[supportedChainId], SimplePREApplicationABI)
+  return useThreshold().multiAppStaking.pre.contract
 }

From 106d739fdc3da39220b006a019fd488c3bc29a1e Mon Sep 17 00:00:00 2001
From: Rafal Czajkowski <rafal.czajkowski@keep.network>
Date: Thu, 10 Nov 2022 15:42:58 +0100
Subject: [PATCH 5/6] Add unit tests for PRE service

---
 .../applications/__tests__/pre.test.ts        | 84 +++++++++++++++++++
 src/threshold-ts/applications/pre/index.ts    |  2 +-
 2 files changed, 85 insertions(+), 1 deletion(-)
 create mode 100644 src/threshold-ts/applications/__tests__/pre.test.ts

diff --git a/src/threshold-ts/applications/__tests__/pre.test.ts b/src/threshold-ts/applications/__tests__/pre.test.ts
new file mode 100644
index 000000000..497f4bca9
--- /dev/null
+++ b/src/threshold-ts/applications/__tests__/pre.test.ts
@@ -0,0 +1,84 @@
+import { BigNumber, ethers } from "ethers"
+import { EthereumConfig } from "../../types"
+import { IPRE, PRE, PRE_ADDRESSESS } from "../pre"
+import SimplePREApplicationABI from "../pre/abi.json"
+import { AddressZero, getContract, isAddressZero } from "../../utils"
+
+jest.mock("../../utils", () => ({
+  ...(jest.requireActual("../../utils") as {}),
+  getContract: jest.fn(),
+}))
+
+describe("PRE application wrapper test", () => {
+  let pre: IPRE
+  let config: EthereumConfig
+
+  const account = "0xaC1933A3Ee78A26E16030801273fBa250631eD5f"
+
+  let mockPREContract: {
+    address: string
+    stakingProviderInfo: jest.MockedFn<any>
+  }
+
+  beforeEach(() => {
+    config = {
+      providerOrSigner: {} as ethers.providers.Provider,
+      chainId: 1,
+      account,
+    }
+    mockPREContract = {
+      address: PRE_ADDRESSESS[config.chainId],
+      stakingProviderInfo: jest.fn(),
+    }
+    ;(getContract as jest.Mock).mockImplementation(() => mockPREContract)
+    pre = new PRE(config)
+  })
+
+  test("should create the PRE instance correctly", () => {
+    expect(getContract).toHaveBeenCalledWith(
+      PRE_ADDRESSESS[config.chainId],
+      SimplePREApplicationABI,
+      config.providerOrSigner,
+      config.account
+    )
+    expect(pre).toBeInstanceOf(PRE)
+    expect(pre.getStakingProviderAppInfo).toBeDefined()
+    expect(pre.contract).toBe(mockPREContract)
+    expect(pre.address).toBe(mockPREContract.address)
+  })
+
+  test("should throw an error if pass unsupported chain id to constructor", () => {
+    expect(() => {
+      new PRE({ chainId: 123456, providerOrSigner: config.providerOrSigner })
+    }).toThrowError("Unsupported chain id")
+  })
+
+  test.each`
+    operatorAddress | operatorConfirmed | testMessage
+    ${AddressZero}  | ${false}          | ${"not set"}
+    ${account}      | ${true}           | ${"set"}
+  `(
+    "should return the staking provider app info if an operator is $testMessage",
+    async ({ operatorAddress, operatorConfirmed }) => {
+      const mockContractResult = {
+        operator: operatorAddress,
+        operatorConfirmed: operatorConfirmed,
+        operatorStartTimestamp: BigNumber.from(123),
+      }
+      mockPREContract.stakingProviderInfo.mockResolvedValue(mockContractResult)
+
+      const result = await pre.getStakingProviderAppInfo(account)
+
+      expect(mockPREContract.stakingProviderInfo).toHaveBeenCalledWith(account)
+      expect(result).toEqual({
+        operator: mockContractResult.operator,
+        isOperatorConfirmed: mockContractResult.operatorConfirmed,
+        operatorStartTimestamp:
+          mockContractResult.operatorStartTimestamp.toString(),
+        isOperatorMapped:
+          !isAddressZero(mockContractResult.operator) &&
+          mockContractResult.operatorConfirmed,
+      })
+    }
+  )
+})
diff --git a/src/threshold-ts/applications/pre/index.ts b/src/threshold-ts/applications/pre/index.ts
index 90c2a72d2..28ae2426a 100644
--- a/src/threshold-ts/applications/pre/index.ts
+++ b/src/threshold-ts/applications/pre/index.ts
@@ -3,7 +3,7 @@ import SimplePREApplicationABI from "./abi.json"
 import { AddressZero, isAddressZero, getContract } from "../../utils"
 import { EthereumConfig } from "../../types"
 
-const PRE_ADDRESSESS = {
+export const PRE_ADDRESSESS = {
   // https://etherscan.io/address/0x7E01c9c03FD3737294dbD7630a34845B0F70E5Dd
   1: "0x7E01c9c03FD3737294dbD7630a34845B0F70E5Dd",
   // https://goerli.etherscan.io/address/0x829fdCDf6Be747FEA37518fBd83dF70EE371fCf2

From 7e17f7c0b5d57ea777f2246af7a86cb73b5d37fd Mon Sep 17 00:00:00 2001
From: Rafal Czajkowski <rafal.czajkowski@keep.network>
Date: Thu, 10 Nov 2022 15:44:42 +0100
Subject: [PATCH 6/6] Fix failing tests for mas service

---
 src/threshold-ts/mas/__test__/mas.test.ts | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/threshold-ts/mas/__test__/mas.test.ts b/src/threshold-ts/mas/__test__/mas.test.ts
index 88760890e..679cb62af 100644
--- a/src/threshold-ts/mas/__test__/mas.test.ts
+++ b/src/threshold-ts/mas/__test__/mas.test.ts
@@ -6,12 +6,18 @@ import { Application } from "../../applications"
 import { IMulticall } from "../../multicall"
 import { IStaking } from "../../staking"
 import { EthereumConfig } from "../../types"
+import { IPRE, PRE } from "../../applications/pre"
 
 jest.mock("../../applications", () => ({
   ...(jest.requireActual("../../applications") as {}),
   Application: jest.fn(),
 }))
 
+jest.mock("../../applications/pre", () => ({
+  ...(jest.requireActual("../../applications/pre") as {}),
+  PRE: jest.fn(),
+}))
+
 jest.mock("@keep-network/random-beacon/artifacts/RandomBeacon.json", () => ({
   address: "0x1",
   abi: [],
@@ -27,6 +33,8 @@ describe("Multi app staking test", () => {
   let multicall: IMulticall
   let config: EthereumConfig
   let mas: MultiAppStaking
+  let pre: IPRE
+
   const app1 = {
     address: WalletRegistry.address,
     contract: { interface: {} },
@@ -38,9 +46,11 @@ describe("Multi app staking test", () => {
 
   beforeEach(() => {
     staking = {} as IStaking
-    multicall = { aggregate: jest.fn() } as IMulticall
+    multicall = { aggregate: jest.fn() } as unknown as IMulticall
+    pre = {} as unknown as IPRE
     ;(Application as unknown as jest.Mock).mockReturnValueOnce(app1)
     ;(Application as unknown as jest.Mock).mockReturnValueOnce(app2)
+    ;(PRE as unknown as jest.Mock).mockResolvedValue(pre)
     config = { chainId: 1, providerOrSigner: {} as providers.Provider }
     mas = new MultiAppStaking(staking, multicall, config)
   })
@@ -57,8 +67,10 @@ describe("Multi app staking test", () => {
       abi: WalletRegistry.abi,
       ...config,
     })
+    expect(PRE).toHaveBeenCalledWith(config)
     expect(mas.randomBeacon).toBeDefined()
     expect(mas.ecdsa).toBeDefined()
+    expect(mas.pre).toBeDefined()
   })
 
   test("should return the supported apps authroziation parameters", async () => {