Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add checks against contract no funds and full slots cases #162

Merged
merged 29 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
825583d
chore: Update package.json and yarn.lock with zustand dependency
mohandast52 May 30, 2024
be4eb87
chore: Add StakingValidations component and getStakingContractInfo fu…
mohandast52 May 31, 2024
cb35f72
chore: Update StakingContractInfo type in Autonolas.ts
mohandast52 May 31, 2024
788edae
chore: Add useStakingContractInfo hook for fetching staking contract …
mohandast52 May 31, 2024
93c93e3
chore: Refactor useStakingContractInfo hook and fetchStakingContractI…
mohandast52 May 31, 2024
9c77e1c
chore: Refactor StakingValidations component and fetchStakingContract…
mohandast52 May 31, 2024
1cafb91
chore: Add StakingValidations component to Main
mohandast52 May 31, 2024
df6c489
feat: Add canStartAgent check to MainHeader component
mohandast52 May 31, 2024
79108c8
chore: Remove unused maxNumServices property in useStakingContractInf…
mohandast52 May 31, 2024
4a384ab
conflict fixes
mohandast52 Jun 3, 2024
073d18b
Merge branch 'main' of github.com-personal:valory-xyz/olas-operate-ap…
mohandast52 Jun 5, 2024
abb11e2
feat: Update file paths and imports for MainHeader and StakingValidat…
mohandast52 Jun 6, 2024
f126199
Merge branch 'main' of github.com-personal:valory-xyz/olas-operate-ap…
mohandast52 Jun 26, 2024
936fe4a
chore: Update StakingContractInfo to use serviceIds instead of getSer…
mohandast52 Jun 26, 2024
ee44d09
chore: Update MainHeader component with new features and improvements
mohandast52 Jun 26, 2024
83a2358
chore: Update AgentEvictedPopover content
mohandast52 Jun 26, 2024
6556a8d
chore: Implement isAgentEvicted flag in StakingContractInfo store
mohandast52 Jun 26, 2024
696f71d
chore: Remove StakingValidations component from MainHeader
mohandast52 Jun 26, 2024
2d95886
chore: Update MainHeader component with new features and improvements…
mohandast52 Jun 26, 2024
2cc967a
chore: Add FirstRunModal component to MainHeader
mohandast52 Jun 26, 2024
630b06a
chore: Refactor MainHeader component and dependencies
mohandast52 Jun 27, 2024
98c7d2f
chore: Update MainHeader component with useStakingContractInfo hook
mohandast52 Jun 27, 2024
f956a4a
Refactor MainHeader component and dependencies
mohandast52 Jun 27, 2024
6e18beb
chore: move to constants and components
mohandast52 Jun 27, 2024
905304f
Refactor MainHeader component and dependencies
mohandast52 Jul 1, 2024
0300647
refractor: StakingContractProvider context added
mohandast52 Jul 1, 2024
8466d40
refractor: remove zustand
mohandast52 Jul 1, 2024
fbbfbf9
chore: move to variable
mohandast52 Jul 1, 2024
a1605a1
chore: address review changes
mohandast52 Jul 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frontend/components/Main/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { MainHeader } from './MainHeader';
import { MainNeedsFunds } from './MainNeedsFunds';
import { MainOlasBalance } from './MainOlasBalance';
import { MainRewards } from './MainRewards';
import { StakingValidations } from './StakingValidations';

export const Main = () => {
const { goto } = usePageState();
Expand Down Expand Up @@ -40,6 +41,7 @@ export const Main = () => {
style={{ borderTopColor: 'transparent' }}
>
<Flex vertical>
<StakingValidations />
<MainOlasBalance />
<MainRewards />
<KeepAgentRunning />
Expand Down
11 changes: 10 additions & 1 deletion frontend/components/Main/MainHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { useWallet } from '@/hooks/useWallet';
import { ServicesService } from '@/service';
import { WalletService } from '@/service/Wallet';

import { useStakingContractInfo } from '../store/stackingContractInfo';

const { Text } = Typography;

const LOADING_MESSAGE =
Expand All @@ -38,6 +40,7 @@ export const MainHeader = () => {
isBalanceLoaded,
setIsPaused: setIsBalancePollingPaused,
} = useBalance();
const { canStartAgent } = useStakingContractInfo();

const safeOlasBalanceWithStaked = useMemo(() => {
if (safeBalance?.OLAS === undefined) return;
Expand Down Expand Up @@ -259,7 +262,12 @@ export const MainHeader = () => {
}

return (
<Button type="primary" size="large" onClick={handleStart}>
<Button
type="primary"
size="large"
onClick={handleStart}
disabled={!canStartAgent}
>
Start agent
</Button>
);
Expand All @@ -273,6 +281,7 @@ export const MainHeader = () => {
services,
storeState?.isInitialFunded,
totalEthBalance,
canStartAgent,
]);

return (
Expand Down
70 changes: 70 additions & 0 deletions frontend/components/Main/StakingValidations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Flex, Typography } from 'antd';
import { useEffect } from 'react';

import { Alert } from '../common/Alert';
import { useStakingContractInfo } from '../store/stackingContractInfo';
import { CardSection } from '../styled/CardSection';

const { Text, Paragraph } = Typography;

const COVER_PREV_BLOCK_BORDER_STYLE = { marginBottom: '-1px' };

export const StakingValidations = () => {
const {
hasEnoughServiceSlots,
isRewardsAvailable,
isStakingContractInfoLoading,
fetchStakingContractInfo,
} = useStakingContractInfo();

useEffect(() => {
fetchStakingContractInfo();
}, [fetchStakingContractInfo]);

if (isStakingContractInfoLoading) return null;

if (!isRewardsAvailable) {
return (
<CardSection style={COVER_PREV_BLOCK_BORDER_STYLE}>
<Alert
showIcon
message={
<Flex vertical gap={4}>
<Text className="font-weight-600 mb-4">No rewards available</Text>
<Paragraph className="mb-4">
There are no rewards available for staking.
</Paragraph>
</Flex>
}
type="error"
fullWidth
/>
</CardSection>
);
}

if (!hasEnoughServiceSlots) {
return (
<CardSection style={COVER_PREV_BLOCK_BORDER_STYLE}>
<Alert
showIcon
message={
<Flex vertical gap={4}>
<Text className="font-weight-600 mb-4">
Not enough service slots
</Text>
<Paragraph className="mb-4">
You have reached the maximum number of services allowed for
staking.
</Paragraph>
</Flex>
}
type="error"
fullWidth
/>
</CardSection>
);
}

return null;
};
47 changes: 47 additions & 0 deletions frontend/components/store/stackingContractInfo.ts
Copy link
Collaborator

@truemiller truemiller Jun 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

either move this to hooks folder (as it exports a hook)
or move the store folder to the root, it does not contain components

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the file name has typo too :/

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • moved to the root folder
  • but I still believe we should keep related components/hooks/functions together. In this case, it should be in MainHeader since no other component uses it. We can move it if other components need it in the future instead of putting everything in the hooks or functions folder. Otherwise, it will eventually become cluttered and difficult to manage.

Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { create } from 'zustand';

import { AutonolasService } from '@/service/Autonolas';

const initialState = {
isStakingContractInfoLoading: true,
isRewardsAvailable: false,
hasEnoughServiceSlots: false,
canStartAgent: false,
};

export const useStakingContractInfo = create<{
isStakingContractInfoLoading: boolean;
isRewardsAvailable: boolean;
hasEnoughServiceSlots: boolean;
canStartAgent: boolean;
fetchStakingContractInfo: () => Promise<void>;
}>((set) => {
return {
...initialState,
isStakingContractInfoLoading: true,
isRewardsAvailable: false,
fetchStakingContractInfo: async () => {
try {
set({ isStakingContractInfoLoading: true });
const info = await AutonolasService.getStakingContractInfo();

if (!info) return;

const { availableRewards, maxNumServices, getServiceIds } = info;
const isRewardsAvailable = availableRewards > 0;
const hasEnoughServiceSlots = getServiceIds.length < maxNumServices;
const canStartAgent = isRewardsAvailable && hasEnoughServiceSlots;

set({
isRewardsAvailable,
hasEnoughServiceSlots,
canStartAgent,
});
} catch (error) {
console.error('Failed to fetch staking contract info', error);
} finally {
set({ isStakingContractInfoLoading: false });
}
},
};
});
3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"react-canvas-confetti": "1.2.1",
"sass": "^1.72.0",
"styled-components": "^6.1.8",
"usehooks-ts": "^2.14.0"
"usehooks-ts": "^2.14.0",
"zustand": "^4.5.2"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.4.2",
Expand Down
36 changes: 34 additions & 2 deletions frontend/service/Autonolas.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ethers } from 'ethers';
import { BigNumber, ethers } from 'ethers';
import { Contract as MulticallContract } from 'ethers-multicall';

import {
Expand All @@ -18,7 +18,7 @@ import {
} from '@/constants';
import { gnosisMulticallProvider } from '@/constants/providers';
import { ServiceRegistryL2ServiceState } from '@/enums';
import { Address, StakingRewardsInfo } from '@/types';
import { Address, StakingContractInfo, StakingRewardsInfo } from '@/types';

const REQUIRED_MECH_REQUESTS_SAFETY_MARGIN = 1;

Expand Down Expand Up @@ -146,6 +146,37 @@ const getAvailableRewardsForEpoch = async (): Promise<number | undefined> => {
return rewardsPerSecond * livenessPeriod;
};

/**
* function to get the staking contract info
*/
const getStakingContractInfo = async (): Promise<
StakingContractInfo | undefined
> => {
const contractCalls = [
serviceStakingTokenMechUsageContract.availableRewards(),
serviceStakingTokenMechUsageContract.maxNumServices(),
serviceStakingTokenMechUsageContract.getServiceIds(),
];

await gnosisMulticallProvider.init();

const multicallResponse = await gnosisMulticallProvider.all(contractCalls);
const [availableRewardsInBN, maxNumServicesInBN, getServiceIdsInBN] =
multicallResponse;

const availableRewards = parseFloat(
ethers.utils.formatUnits(availableRewardsInBN, 18),
);
const getServiceIds = getServiceIdsInBN.map((id: BigNumber) => id.toNumber());
const maxNumServices = maxNumServicesInBN.toNumber();

return {
availableRewards,
maxNumServices,
getServiceIds,
};
};

const getServiceRegistryInfo = async (
operatorAddress: Address, // generally masterSafeAddress
serviceId: number,
Expand Down Expand Up @@ -188,4 +219,5 @@ export const AutonolasService = {
getAgentStakingRewardsInfo,
getAvailableRewardsForEpoch,
getServiceRegistryInfo,
getStakingContractInfo,
};
6 changes: 6 additions & 0 deletions frontend/types/Autonolas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@ export type StakingRewardsInfo = {
accruedServiceStakingRewards: number;
minimumStakedAmount: number;
};

export type StakingContractInfo = {
availableRewards: number;
maxNumServices: number;
getServiceIds: number[];
};
14 changes: 14 additions & 0 deletions frontend/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5621,6 +5621,7 @@ string-length@^4.0.1:
strip-ansi "^6.0.0"

"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
name string-width-cjs
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand Down Expand Up @@ -6019,6 +6020,11 @@ url-parse@^1.5.3:
querystringify "^2.1.1"
requires-port "^1.0.0"

[email protected]:
version "1.2.0"
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==

usehooks-ts@^2.14.0:
version "2.16.0"
resolved "https://registry.yarnpkg.com/usehooks-ts/-/usehooks-ts-2.16.0.tgz#31deaa2f1147f65666aae925bd890b54e63b0d3f"
Expand Down Expand Up @@ -6142,6 +6148,7 @@ word-wrap@^1.2.5:
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==

"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
name wrap-ansi-cjs
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
Expand Down Expand Up @@ -6229,3 +6236,10 @@ yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==

zustand@^4.5.2:
version "4.5.2"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.2.tgz#fddbe7cac1e71d45413b3682cdb47b48034c3848"
integrity sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==
dependencies:
use-sync-external-store "1.2.0"
Loading