Skip to content

Commit

Permalink
feat: Inform users that there are no available slots on staking contr…
Browse files Browse the repository at this point in the history
…act and prompt them to switch (#421)

* fix: improve error messages in AgentButton component

* feat: add NoAvailableSlotsOnTheContract component

* feat: integrate NoAvailableSlotsOnTheContract component and enhance service slot checks

* feat: implement useCanStartUpdateStakingContract hook and update related components

* feat: add available slots display and max-width styling to StakingContractDetails

* feat: add POPOVER_WIDTH_LARGE constant and apply max-width styling to CountdownUntilMigration component

* fix: correct spelling of 'occurred' in error notifications in AgentButton component

* refactor: rename useCanStartUpdateStakingContract hook to useCanUpdateStakingContract and update related components

* feat: update StakingContractUpdate and NoAvailableSlotsOnTheContract components to accept stakingProgramId prop and remove useCanUpdateStakingContract hook

* chore: bump version to 0.1.0-rc186 in package.json and pyproject.toml
  • Loading branch information
mohandast52 authored Nov 5, 2024
1 parent 7c0d5b8 commit f7fcd5e
Show file tree
Hide file tree
Showing 14 changed files with 120 additions and 21 deletions.
15 changes: 11 additions & 4 deletions frontend/components/MainPage/header/AgentButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const AgentRunningButton = () => {
await ServicesService.stopDeployment(service.hash);
} catch (error) {
console.error(error);
showNotification?.('Error while stopping agent');
showNotification?.('Some error occurred while stopping agent');
} finally {
// Resume polling, will update to correct status regardless of success
setIsServicePollingPaused(false);
Expand Down Expand Up @@ -145,6 +145,7 @@ const AgentNotRunningButton = () => {
isAgentEvicted,
setIsPaused: setIsStakingContractInfoPollingPaused,
updateActiveStakingContractInfo,
hasEnoughServiceSlots,
} = useStakingContractInfo();
const { activeStakingProgramId, defaultStakingProgramId } =
useStakingProgram();
Expand Down Expand Up @@ -192,7 +193,7 @@ const AgentNotRunningButton = () => {
} catch (error) {
console.error(error);
setServiceStatus(undefined);
showNotification?.('Error while creating safe');
showNotification?.('Some error occurred while creating safe');
setIsStakingContractInfoPollingPaused(false);
setIsServicePollingPaused(false);
setIsBalancePollingPaused(false);
Expand All @@ -211,7 +212,7 @@ const AgentNotRunningButton = () => {
} catch (error) {
console.error(error);
setServiceStatus(undefined);
showNotification?.('Error while deploying service');
showNotification?.('Some error occurred while deploying service');
setIsServicePollingPaused(false);
setIsBalancePollingPaused(false);
setIsStakingContractInfoPollingPaused(false);
Expand All @@ -223,7 +224,9 @@ const AgentNotRunningButton = () => {
showNotification?.(`Your agent is running!`);
} catch (error) {
console.error(error);
showNotification?.('Error while showing "running" notification');
showNotification?.(
'Some error occurred while showing "running" notification',
);
}

// Can assume successful deployment
Expand Down Expand Up @@ -278,6 +281,9 @@ const AgentNotRunningButton = () => {

if (!requiredOlas) return false;

// If no slots available, agent cannot be started
if (!hasEnoughServiceSlots) return false;

// case where service exists & user has initial funded
if (service && storeState?.isInitialFunded) {
if (!safeOlasBalanceWithStaked) return false;
Expand All @@ -302,6 +308,7 @@ const AgentNotRunningButton = () => {
requiredOlas,
totalEthBalance,
isLowBalance,
hasEnoughServiceSlots,
]);

const buttonProps: ButtonProps = {
Expand Down
7 changes: 6 additions & 1 deletion frontend/components/MainPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useBalance } from '@/hooks/useBalance';
import { useMasterSafe } from '@/hooks/useMasterSafe';
import { usePageState } from '@/hooks/usePageState';
import { useServices } from '@/hooks/useServices';
import { useStakingContractInfo } from '@/hooks/useStakingContractInfo';
import { useStakingProgram } from '@/hooks/useStakingProgram';

import { MainHeader } from './header';
Expand All @@ -26,6 +27,7 @@ export const Main = () => {
const { updateServicesState } = useServices();
const { updateBalances, isLoaded, setIsLoaded } = useBalance();
const { activeStakingProgramId: currentStakingProgram } = useStakingProgram();
const { hasEnoughServiceSlots } = useStakingContractInfo();

useEffect(() => {
if (!isLoaded) {
Expand All @@ -37,6 +39,7 @@ export const Main = () => {
const hideMainOlasBalanceTopBorder = [
!backupSafeAddress,
currentStakingProgram === StakingProgramId.Alpha,
!hasEnoughServiceSlots,
].some((condition) => !!condition);

return (
Expand Down Expand Up @@ -71,7 +74,9 @@ export const Main = () => {
<MainOlasBalance isBorderTopVisible={!hideMainOlasBalanceTopBorder} />
<RewardsSection />
<KeepAgentRunningSection />
<StakingContractUpdate />
{currentStakingProgram && (
<StakingContractUpdate stakingProgramId={currentStakingProgram} />
)}
<GasBalanceSection />
<MainNeedsFunds />
<AddFundsSection />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Flex, Typography } from 'antd';

import { useMigrate } from '@/components/ManageStakingPage/StakingContractSection/useMigrate';
import { Pages } from '@/enums/PageState';
import { StakingProgramId } from '@/enums/StakingProgram';
import { usePageState } from '@/hooks/usePageState';
import { useStakingContractInfo } from '@/hooks/useStakingContractInfo';

import { CustomAlert } from '../../../Alert';

const { Text } = Typography;

type NoAvailableSlotsOnTheContractProps = {
stakingProgramId: StakingProgramId;
};
export const NoAvailableSlotsOnTheContract = ({
stakingProgramId,
}: NoAvailableSlotsOnTheContractProps) => {
const { goto } = usePageState();
const { hasEnoughServiceSlots } = useStakingContractInfo();
const { canUpdateStakingContract } = useMigrate(stakingProgramId);

if (hasEnoughServiceSlots) return null;

return (
<CustomAlert
type="warning"
fullWidth
showIcon
message={
<Flex justify="space-between" gap={4} vertical>
<Text className="font-weight-600">
No available slots on the contract
</Text>
<span className="text-sm">
Select a contract with available slots to be able to start your
agent.
</span>
{canUpdateStakingContract && (
<Text
className="pointer hover-underline text-primary text-sm"
onClick={() => goto(Pages.ManageStaking)}
>
Change staking contract
</Text>
)}
</Flex>
}
/>
);
};
9 changes: 9 additions & 0 deletions frontend/components/MainPage/sections/AlertSections/index.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
import { CardSection } from '@/components/styled/CardSection';
import { useStakingProgram } from '@/hooks/useStakingProgram';

import { AddBackupWalletAlert } from './AddBackupWalletAlert';
import { AvoidSuspensionAlert } from './AvoidSuspensionAlert';
import { LowTradingBalanceAlert } from './LowTradingBalanceAlert';
import { NewStakingProgramAlert } from './NewStakingProgramAlert';
import { NoAvailableSlotsOnTheContract } from './NoAvailableSlotsOnTheContract';
import { UpdateAvailableAlert } from './UpdateAvailableAlert';

export const AlertSections = () => {
const { activeStakingProgramId } = useStakingProgram();

return (
<CardSection vertical>
<UpdateAvailableAlert />
<AddBackupWalletAlert />
<NewStakingProgramAlert />
<AvoidSuspensionAlert />
<LowTradingBalanceAlert />
{activeStakingProgramId && (
<NoAvailableSlotsOnTheContract
stakingProgramId={activeStakingProgramId}
/>
)}
</CardSection>
);
};
19 changes: 7 additions & 12 deletions frontend/components/MainPage/sections/StakingContractUpdate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,32 @@ import { useMemo } from 'react';

import { STAKING_PROGRAM_META } from '@/constants/stakingProgramMeta';
import { Pages } from '@/enums/PageState';
import { useBalance } from '@/hooks/useBalance';
import { useNeedsFunds } from '@/hooks/useNeedsFunds';
import { StakingProgramId } from '@/enums/StakingProgram';
import { usePageState } from '@/hooks/usePageState';
import { useStakingProgram } from '@/hooks/useStakingProgram';

import { useMigrate } from '../../ManageStakingPage/StakingContractSection/useMigrate';
import { CardSection } from '../../styled/CardSection';

const { Text } = Typography;

export const StakingContractUpdate = () => {
type StakingContractUpdateProps = { stakingProgramId: StakingProgramId };
export const StakingContractUpdate = ({
stakingProgramId,
}: StakingContractUpdateProps) => {
const { goto } = usePageState();
const { isBalanceLoaded, isLowBalance } = useBalance();
const { needsInitialFunding } = useNeedsFunds();
const {
activeStakingProgramMeta,
isActiveStakingProgramLoaded,
defaultStakingProgramId,
} = useStakingProgram();
const { canUpdateStakingContract } = useMigrate(stakingProgramId);

const stakingContractName = useMemo(() => {
if (activeStakingProgramMeta) return activeStakingProgramMeta.name;
return STAKING_PROGRAM_META[defaultStakingProgramId].name;
}, [activeStakingProgramMeta, defaultStakingProgramId]);

const canUpdateStakingContract = useMemo(() => {
if (!isBalanceLoaded) return false;
if (isLowBalance) return false;
if (needsInitialFunding) return false;
return true;
}, [isBalanceLoaded, isLowBalance, needsInitialFunding]);

const stakingButton = useMemo(() => {
if (!isActiveStakingProgramLoaded) return <Skeleton.Input />;
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { isNil } from 'lodash';
import { useState } from 'react';
import { useInterval } from 'usehooks-ts';

import { POPOVER_WIDTH_LARGE } from '@/constants/width';
import { StakingContractInfo } from '@/types/Autonolas';

const { Text } = Typography;
Expand Down Expand Up @@ -41,7 +42,7 @@ export const CountdownUntilMigration = ({
: countdownDisplayFormat(secondsUntilReady);

return (
<Flex vertical gap={1}>
<Flex vertical gap={1} style={{ maxWidth: POPOVER_WIDTH_LARGE }}>
<Text strong>Can&apos;t switch because you unstaked too recently.</Text>
<Text>This may be because your agent was suspended.</Text>
<Text>Keep running your agent and you&apos;ll be able to switch in</Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Alert, Skeleton } from 'antd';
import { useMemo } from 'react';

import { InfoBreakdownList } from '@/components/InfoBreakdown';
import { NA } from '@/constants/symbols';
import { StakingProgramId } from '@/enums/StakingProgram';
import { useStakingContractInfo } from '@/hooks/useStakingContractInfo';

Expand All @@ -19,13 +20,18 @@ export const StakingContractDetails = ({

const details = stakingContractInfoRecord[stakingProgramId];
return [
{
left: 'Available slots',
right: details.maxNumServices || NA,
},
{
left: 'Rewards per epoch',
right: `~ ${details.rewardsPerWorkPeriod?.toFixed(2)} OLAS`,
},
{
left: 'Estimated Annual Percentage Yield (APY)',
right: `${details.apy}%`,
leftClassName: 'max-width-200',
},
{
left: 'Required OLAS for staking',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useMemo } from 'react';
import { DeploymentStatus } from '@/client';
import { StakingProgramId } from '@/enums/StakingProgram';
import { useBalance } from '@/hooks/useBalance';
import { useNeedsFunds } from '@/hooks/useNeedsFunds';
import { useServices } from '@/hooks/useServices';
import { useServiceTemplates } from '@/hooks/useServiceTemplates';
import { useStakingContractInfo } from '@/hooks/useStakingContractInfo';
Expand Down Expand Up @@ -40,16 +41,19 @@ export const useMigrate = (stakingProgramId: StakingProgramId) => {
isBalanceLoaded,
masterSafeBalance: safeBalance,
totalOlasStakedBalance,
isLowBalance,
} = useBalance();
const { activeStakingProgramId, activeStakingProgramMeta } =
useStakingProgram();
const { needsInitialFunding } = useNeedsFunds();

const {
activeStakingContractInfo,
isServiceStaked,
isServiceStakedForMinimumDuration,
isStakingContractInfoLoaded,
stakingContractInfoRecord,
hasEnoughServiceSlots,
} = useStakingContractInfo();

const stakingContractInfo = stakingContractInfoRecord?.[stakingProgramId];
Expand Down Expand Up @@ -231,8 +235,22 @@ export const useMigrate = (stakingProgramId: StakingProgramId) => {
stakingProgramId,
]);

const canUpdateStakingContract = useMemo(() => {
if (!isBalanceLoaded) return false;
if (isLowBalance) return false;
if (needsInitialFunding) return false;
if (!hasEnoughServiceSlots) return false;
return true;
}, [
isBalanceLoaded,
isLowBalance,
needsInitialFunding,
hasEnoughServiceSlots,
]);

return {
migrateValidation,
firstDeployValidation,
canUpdateStakingContract,
};
};
2 changes: 2 additions & 0 deletions frontend/constants/symbols.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export const NA = 'n/a';

export const UNICODE_SYMBOLS = {
OLAS: '☴',
EXTERNAL_LINK: '↗',
Expand Down
2 changes: 2 additions & 0 deletions frontend/constants/width.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export const MODAL_WIDTH = 412;

export const POPOVER_WIDTH_MEDIUM = 260;

export const POPOVER_WIDTH_LARGE = 340;
4 changes: 3 additions & 1 deletion frontend/styles/globals.scss
Original file line number Diff line number Diff line change
Expand Up @@ -262,10 +262,12 @@ ul.alert-list {
.w-3\/4 {
width: 75% !important;
}

.w-full {
width: 100% !important;
}
.max-width-200 {
max-width: 200px;
}

.loading-ellipses:after {
overflow: hidden;
Expand Down
1 change: 1 addition & 0 deletions frontend/types/Autonolas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type StakingRewardsInfo = {

export type StakingContractInfo = {
availableRewards: number;
/* number of slots available for staking */
maxNumServices: number;
serviceIds: number[];
/** minimum staking duration (in seconds) */
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,5 @@
"download-binaries": "sh download_binaries.sh",
"build:pearl": "sh build_pearl.sh"
},
"version": "0.1.0-rc184"
"version": "0.1.0-rc186"
}
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "olas-operate-middleware"
version = "0.1.0-rc184"
version = "0.1.0-rc186"
description = ""
authors = ["David Vilela <[email protected]>", "Viraj Patel <[email protected]>"]
readme = "README.md"
Expand Down

0 comments on commit f7fcd5e

Please sign in to comment.