Skip to content

Commit

Permalink
SOV-2482: Automatic staking maintenance (#198)
Browse files Browse the repository at this point in the history
* Automatic staking maintenance

* Fix build
  • Loading branch information
tiltom authored Jun 2, 2023
1 parent 92ecdee commit d1e410c
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 2 deletions.
2 changes: 1 addition & 1 deletion src/config/sovryn-onboard-chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,6 @@ export const defaultChainId = (
isMainnet ? ChainIds.RSK_MAINNET : ChainIds.RSK_TESTNET
) as string;

export const chains: Chain[] = isMainnet ? mainnetChains : testnetChains;
export const chains: Chain[] = [...mainnetChains, ...testnetChains];

setup(chains);
15 changes: 15 additions & 0 deletions src/contracts/abi/Staking.json
Original file line number Diff line number Diff line change
Expand Up @@ -1378,5 +1378,20 @@
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "paused",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]
28 changes: 28 additions & 0 deletions src/pages/Staking/Staking.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import { RewardBlockProps, StakingComponentProps } from './Staking.types';
import wave1Icon from '../../assets/icons/wave1.svg';
import wave2Icon from '../../assets/icons/wave2.svg';
import wave3Icon from '../../assets/icons/wave3.svg';
import maintenanceModeIcon from '../../assets/icons/maintenance-mode.svg';
import { useIsInMaintenance } from './Staking.hooks';

export const StakingComponent = ({
rewards,
Expand All @@ -31,11 +33,14 @@ export const StakingComponent = ({
}: StakingComponentProps) => {
const [showAddStakeDialog, setShowAddStakeDialog] = useState(false);

const isInMaintenance = useIsInMaintenance();

const handleOpenDialog = () => setShowAddStakeDialog(true);
const handleCloseDialog = () => setShowAddStakeDialog(false);

return (
<Box sx={{ position: 'relative' }}>
{isInMaintenance && <MaintenanceModeOverlay />}
<Breadcrumbs links={[{ title: UrlNames.Staking }]} />
<PageAligner>
<Box
Expand Down Expand Up @@ -123,3 +128,26 @@ const RewardBlock = ({ amount, asset, usdAmount }: RewardBlockProps) => (
</Button>
</Box>
);

const MaintenanceModeOverlay = () => (
<Box
sx={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
zIndex: 1000,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.8)',
}}
>
<img
src={maintenanceModeIcon}
alt="Staking is currently under maintenance"
width={600}
/>
</Box>
);
18 changes: 18 additions & 0 deletions src/pages/Staking/Staking.hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { getCurrentTimestamp } from '../../utils/helpers';
import {
providerSelector,
stakingContractSelector,
stakingContractSelectorDefaultChain,
} from '../../store/app/app.selectors';
import { UseEstimateFeeConfig } from './Staking.types';

Expand Down Expand Up @@ -140,3 +141,20 @@ export const useEstimateFee = ({

return estimatedFee;
};

export const useIsInMaintenance = () => {
const [isInMaintenance, setIsInMaintenance] = useState(false);

const staking = useSelector(stakingContractSelectorDefaultChain);

useEffect(() => {
const fetchIsPaused = async () => {
const isPaused = await staking?.paused();

setIsInMaintenance(isPaused || false);
};
fetchIsPaused();
}, [staking]);

return isInMaintenance;
};
60 changes: 59 additions & 1 deletion src/store/app/app.selectors.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { utils } from 'ethers';
import { ContractInterface, ethers, utils } from 'ethers';
import { createSelector } from '@reduxjs/toolkit';

import {
Provider as MulticallProvider,
setMulticallAddress,
} from 'ethers-multicall';
import { ChainIds, getProvider } from '@sovryn/ethers-provider';
import { RootState } from '..';
import {
contractsAddresses,
Expand Down Expand Up @@ -177,6 +178,43 @@ export const addressesSelector = createSelector(
}
);

const getDefaultChainSelector = createSelector(
testnetMainnetSelector,
(environment) => {
const chain =
!environment || environment === 'mainnet'
? ChainIds.RSK_MAINNET
: ChainIds.RSK_TESTNET;

return chain;
}
);

export const addressesSelectorDefaultChain = createSelector(
getDefaultChainSelector,
(defaultChain) => {
if (!defaultChain) return undefined;

const chain =
defaultChain === ChainIds.RSK_MAINNET
? ChainEnum.RSK
: ChainEnum.RSK_TESTNET;

return contractsAddresses[chain];
}
);

const getDefaultChainProviderSelector = createSelector(
getDefaultChainSelector,
(defaultChain) => {
if (!defaultChain) {
return undefined;
}

return getProvider(defaultChain);
}
);

const createContractSelector = <Factory extends BaseContractFactory>(
factory: Factory,
name: keyof ContractsForNetwork
Expand All @@ -192,10 +230,30 @@ const createContractSelector = <Factory extends BaseContractFactory>(
}
);

const createDefaultChainContractSelector = (
abi: ContractInterface,
name: keyof ContractsForNetwork
) =>
createSelector(
[getDefaultChainProviderSelector, addressesSelectorDefaultChain],
(chainProvider, addresses) => {
if (!chainProvider || !addresses) {
return undefined;
}

return new ethers.Contract(addresses[name], abi, chainProvider);
}
);

// TODO: All contracts from contracts.ts should be created by createDefaultChainContractSelector
export const stakingContractSelectorDefaultChain =
createDefaultChainContractSelector(Staking__factory.abi, 'staking');

export const stakingContractSelector = createContractSelector(
Staking__factory,
'staking'
);

export const fishTokenSelector = createContractSelector(
ERC20__factory,
'fishToken'
Expand Down

0 comments on commit d1e410c

Please sign in to comment.