diff --git a/solidity/dashboard/src/components/copy-stake-steps/Step1.jsx b/solidity/dashboard/src/components/copy-stake-steps/Step1.jsx
index f788cc89dc..f8f7d5f8ff 100644
--- a/solidity/dashboard/src/components/copy-stake-steps/Step1.jsx
+++ b/solidity/dashboard/src/components/copy-stake-steps/Step1.jsx
@@ -1,4 +1,4 @@
-import React, { useEffect } from "react"
+import React from "react"
import TokenAmount from "../TokenAmount"
import Button from "../Button"
import { KeepLoadingIndicator } from "../Loadable"
@@ -26,7 +26,6 @@ const styles = {
}
const CopyStakeStep1 = ({
- fetchDelegations,
isFetching,
delegations,
decrementStep,
@@ -34,10 +33,6 @@ const CopyStakeStep1 = ({
onSelectDelegation,
selectedDelegation,
}) => {
- useEffect(() => {
- fetchDelegations()
- }, [fetchDelegations])
-
return (
<>
Stake balances to be upgraded.
diff --git a/solidity/dashboard/src/pages/CopyStakePage.jsx b/solidity/dashboard/src/pages/CopyStakePage.jsx
index 077d11520d..1783860acd 100644
--- a/solidity/dashboard/src/pages/CopyStakePage.jsx
+++ b/solidity/dashboard/src/pages/CopyStakePage.jsx
@@ -12,7 +12,6 @@ import {
DECREMENT_STEP,
SET_STRATEGY,
SET_DELEGATION,
- FETCH_DELEGATIONS_FROM_OLD_STAKING_CONTRACT_REQUEST,
RESET_COPY_STAKE_FLOW,
} from "../actions"
import { connect } from "react-redux"
@@ -72,7 +71,6 @@ const CopyStakePage = ({
isFetching={oldDelegationsFetching}
selectedDelegation={selectedDelegation}
onSelectDelegation={setDelegation}
- fetchDelegations={fetchOldDelegations}
/>
)
case 2:
@@ -135,8 +133,6 @@ const mapDispatchToProps = (dispatch) => {
dispatch({ type: SET_STRATEGY, payload: strategy }),
setDelegation: (delegation) =>
dispatch({ type: SET_DELEGATION, payload: delegation }),
- fetchOldDelegations: () =>
- dispatch({ type: FETCH_DELEGATIONS_FROM_OLD_STAKING_CONTRACT_REQUEST }),
undelegateOldStake: (delegation) =>
dispatch({ type: "copy-stake/undelegate_request", payload: delegation }),
recoverOldStake: (delegation) =>
diff --git a/solidity/dashboard/src/pages/TokensPageContainer.jsx b/solidity/dashboard/src/pages/TokensPageContainer.jsx
index b437cd3fb6..0f74a84e46 100644
--- a/solidity/dashboard/src/pages/TokensPageContainer.jsx
+++ b/solidity/dashboard/src/pages/TokensPageContainer.jsx
@@ -18,6 +18,8 @@ import {
TOP_UP_INITIATED,
TOP_UP_COMPLETED,
} from "../reducers/tokens-page.reducer.js"
+import { FETCH_DELEGATIONS_FROM_OLD_STAKING_CONTRACT_REQUEST } from "../actions"
+import { connect } from "react-redux"
import {
TOKEN_STAKING_CONTRACT_NAME,
TOKEN_GRANT_CONTRACT_NAME,
@@ -25,6 +27,7 @@ import {
} from "../constants/constants"
import { isSameEthAddress } from "../utils/general.utils"
import { sub, add } from "../utils/arithmetics.utils"
+import { isEmptyArray } from "../utils/array.utils"
import moment from "moment"
import {
createManagedGrantContractInstance,
@@ -38,7 +41,7 @@ import Button from "../components/Button"
import { useModal } from "../hooks/useModal"
import CopyStakePage from "./CopyStakePage"
-const TokensPageContainer = () => {
+const TokensPageContainer = ({ oldDelegations, fetchOldDelegations }) => {
useSubscribeToStakedEvent()
useSubscribeToUndelegatedEvent()
useSubscribeToRecoveredStakeEvent()
@@ -55,24 +58,30 @@ const TokensPageContainer = () => {
}
}, [hash, dispatch])
+ useEffect(() => {
+ fetchOldDelegations()
+ }, [fetchOldDelegations])
+
const { openModal } = useModal()
return (
<>
-
-
-
+
+
+ )}
@@ -83,14 +92,6 @@ const TokensPageContainer = () => {
)
}
-const TokensPageContainerWithContext = () => (
-
-
-
-)
-
-export default React.memo(TokensPageContainerWithContext)
-
const useSubscribeToStakedEvent = () => {
const {
initializationPeriod,
@@ -450,3 +451,29 @@ const useSubscribeToTopUpsEvents = () => {
subscribeToTopUpCompleted
)
}
+
+const mapStateToProps = ({ copyStake }) => {
+ const { oldDelegations } = copyStake
+
+ return { oldDelegations }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ fetchOldDelegations: () =>
+ dispatch({ type: FETCH_DELEGATIONS_FROM_OLD_STAKING_CONTRACT_REQUEST }),
+ }
+}
+
+const TokensPageContainerWithRedux = connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(TokensPageContainer)
+
+const TokensPageContainerWithContext = () => (
+
+
+
+)
+
+export default React.memo(TokensPageContainerWithContext)
diff --git a/solidity/dashboard/src/reducers/copy-stake.js b/solidity/dashboard/src/reducers/copy-stake.js
index c349a7b5f7..9cf9e323a3 100644
--- a/solidity/dashboard/src/reducers/copy-stake.js
+++ b/solidity/dashboard/src/reducers/copy-stake.js
@@ -8,6 +8,7 @@ import {
FETCH_DELEGATIONS_FROM_OLD_STAKING_CONTRACT_REQUEST,
RESET_COPY_STAKE_FLOW,
} from "../actions"
+import { isSameEthAddress } from "../utils/general.utils"
export const copyStakeInitialData = {
oldDelegations: [],
@@ -90,6 +91,15 @@ const copyStakeReducer = (state = copyStakeInitialData, action) => {
isProcessing: false,
error: action.payload,
}
+ case "copy-stake/remove_old_delegation": {
+ return {
+ ...state,
+ oldDelegations: state.oldDelegations.filter(
+ (delegation) =>
+ !isSameEthAddress(delegation.operatorAddress, action.payload)
+ ),
+ }
+ }
default:
return state
}
diff --git a/solidity/dashboard/src/sagas/copy-stake.js b/solidity/dashboard/src/sagas/copy-stake.js
index 1386b1a12f..9589365bc5 100644
--- a/solidity/dashboard/src/sagas/copy-stake.js
+++ b/solidity/dashboard/src/sagas/copy-stake.js
@@ -1,4 +1,4 @@
-import { takeLatest, call, put, delay } from "redux-saga/effects"
+import { takeLatest, take, call, put, delay, fork } from "redux-saga/effects"
import {
FETCH_DELEGATIONS_FROM_OLD_STAKING_CONTRACT_REQUEST,
FETCH_DELEGATIONS_FROM_OLD_STAKING_CONTRACT_SUCCESS,
@@ -7,7 +7,7 @@ import {
} from "../actions"
import { fetchOldDelegations } from "../services/staking-port-backer.service"
import { getContractsContext } from "./utils"
-import { sendTransaction } from "./web3"
+import { sendTransaction, createSubcribeToContractEventChannel } from "./web3"
import { CONTRACT_DEPLOY_BLOCK_NUMBER } from "../contracts"
import { showMessage, showCreatedMessage } from "../actions/messages"
import { isEmptyArray } from "../utils/array.utils"
@@ -29,6 +29,8 @@ function* fetchOldStakingDelegations() {
payload: error,
})
}
+
+ yield fork(observeEvents)
}
export function* watchFetchOldStakingContract() {
@@ -186,3 +188,39 @@ export function* watchUndelegateOldStakeRequest() {
export function* watchRecovereOldStakeRequest() {
yield takeLatest("copy-stake/recover_request", recoverFromOldStakingContract)
}
+
+function* observeEvents() {
+ const { oldTokenStakingContract, stakingPortBackerContract } = yield call(
+ getContractsContext
+ )
+
+ yield fork(removeOldDelegationWatcher, oldTokenStakingContract, "Undelegated")
+ yield fork(
+ removeOldDelegationWatcher,
+ stakingPortBackerContract,
+ "StakeCopied"
+ )
+ yield fork(removeOldDelegationWatcher, oldTokenStakingContract, "Recovered")
+}
+
+function* removeOldDelegationWatcher(contract, eventName) {
+ // Create subscription channel.
+ const contractEventCahnnel = yield call(
+ createSubcribeToContractEventChannel,
+ contract,
+ eventName
+ )
+
+ // Observe and dispatch an action that updates copy-stake reducer.
+ while (true) {
+ try {
+ const {
+ returnValues: { operator },
+ } = yield take(contractEventCahnnel)
+ yield put({ type: "copy-stake/remove_old_delegation", payload: operator })
+ } catch (error) {
+ console.error(`Failed subscribing to ${eventName} event`, error)
+ contractEventCahnnel.close()
+ }
+ }
+}
diff --git a/solidity/dashboard/src/sagas/web3.js b/solidity/dashboard/src/sagas/web3.js
index 3f3944a7b6..2cef877454 100644
--- a/solidity/dashboard/src/sagas/web3.js
+++ b/solidity/dashboard/src/sagas/web3.js
@@ -74,6 +74,40 @@ function createTransactionEventChannel(contract, method, args, options) {
}, buffers.expanding())
}
+export function createSubcribeToContractEventChannel(contract, eventName) {
+ const contractHasEvent = contract.options.jsonInterface.find(
+ (entry) => entry.type === "event" && entry.name === eventName
+ )
+ if (!contractHasEvent) {
+ return eventChannel((emit) => {
+ emit(END)
+
+ return () => {}
+ }, buffers.expanding())
+ }
+
+ const eventEmitter = contract.events[eventName]()
+ let eventTxCache = null
+
+ return eventChannel((emit) => {
+ eventEmitter
+ .on("data", (event) => {
+ if (eventTxCache !== event.transactionHash) {
+ eventTxCache = event.transactionHash
+ emit(event)
+ }
+ })
+ .on("error", () => {
+ emit(new Error())
+ emit(END)
+ })
+
+ return () => {
+ eventEmitter.unsubscribe()
+ }
+ })
+}
+
export function* sendTransaction(action) {
const { contract, methodName, args, options } = action.payload