From 114d2c4bcdf7b6383a7b771bb297de743a8fa8ef Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 09:46:32 +0000 Subject: [PATCH 01/28] refactor: useServices for all services, useService for single service --- frontend/hooks/useServices.ts | 162 +++++++++++++++++++--------------- 1 file changed, 93 insertions(+), 69 deletions(-) diff --git a/frontend/hooks/useServices.ts b/frontend/hooks/useServices.ts index 1da28a06..da199297 100644 --- a/frontend/hooks/useServices.ts +++ b/frontend/hooks/useServices.ts @@ -1,82 +1,106 @@ import { useContext } from 'react'; -import { - MiddlewareServiceResponse, - ServiceHash, - ServiceTemplate, -} from '@/client'; -import { CHAIN_CONFIG } from '@/config/chains'; +import { MiddlewareServiceResponse } from '@/client'; import { ServicesContext } from '@/context/ServicesProvider'; -import MulticallService from '@/service/Multicall'; -import { Address } from '@/types/Address'; -import { AddressBooleanRecord } from '@/types/Records'; - -const checkServiceIsFunded = async ( - service: MiddlewareServiceResponse, - serviceTemplate: ServiceTemplate, -): Promise => { - const { - chain_configs: { - [CHAIN_CONFIG.OPTIMISM.chainId]: { - chain_data: { instances, multisig }, - }, - }, - } = service; - - if (!instances || !multisig) return false; - - const addresses = [...instances, multisig]; - - const balances = await MulticallService.getEthBalances(addresses); - - if (!balances) return false; - - const fundRequirements: AddressBooleanRecord = addresses.reduce( - (acc: AddressBooleanRecord, address: Address) => - Object.assign(acc, { - [address]: instances.includes(address) - ? balances[address] > - serviceTemplate.configurations[CHAIN_CONFIG.OPTIMISM.chainId] - .fund_requirements.agent - : balances[address] > - serviceTemplate.configurations[CHAIN_CONFIG.OPTIMISM.chainId] - .fund_requirements.safe, - }), - {}, - ); - - return Object.values(fundRequirements).every((f) => f); -}; +import { ChainId } from '@/enums/Chain'; -export const useServices = () => { - const { services, isFetched: hasInitialLoaded } = useContext(ServicesContext); +// const checkServiceIsFundedOnChain = async ({ +// service, +// chainId, +// }: { +// service: MiddlewareServiceResponse; +// chainId: ChainId; +// }) => { +// if (!service.chain_configs[chainId].chain_data.instances) return false; +// if (!service.chain_configs[chainId].chain_data.multisig) return false; + +// const instances: Address[] = +// service.chain_configs[chainId].chain_data.instances; + +// const multisig: Address = service.chain_configs[chainId].chain_data.multisig; + +// const addresses = [...instances, multisig]; + +// const balances = await MulticallService.getEthBalances(addresses, chainId); + +// if (!balances) return false; + +// const fundRequirements: AddressBooleanRecord = addresses.reduce( +// (acc: AddressBooleanRecord, address: Address) => +// Object.assign(acc, { +// [address]: instances.includes(address) +// ? balances[address] > stakingProgram +// : balances[address] > +// serviceTemplate.configurations[chainId].fund_requirements.safe, +// }), +// {}, +// ); + +// return Object.values(fundRequirements).every((f) => f); +// }; + +// const checkServiceIsFunded = async ( +// service: MiddlewareServiceResponse, +// stakingProgramFundingRequirements: +// ): Promise => { +// // get all the chainIds from the service +// const chainIds: ChainId[] = Object.keys(service.chain_configs).map( +// (chainId) => +chainId, +// ); - const serviceId = - services?.[0]?.chain_configs[CHAIN_CONFIG.OPTIMISM.chainId].chain_data?.token; +// // loop over the chainIds and check if the service is funded +// const instanceAddresses = chainIds.map( +// (chainId) => service.chain_configs[chainId].chain_data.instances, +// ); + +// if (!instances || !multisig) return false; + +// const addresses = [...instances, multisig]; + +// const balances = await MulticallService.getEthBalances(addresses); + +// if (!balances) return false; + +// const fundRequirements: AddressBooleanRecord = addresses.reduce( +// (acc: AddressBooleanRecord, address: Address) => +// Object.assign(acc, { +// [address]: instances.includes(address) +// ? balances[address] > +// serviceTemplate.configurations[CHAIN_CONFIG.OPTIMISM.chainId] +// .fund_requirements.agent +// : balances[address] > +// serviceTemplate.configurations[CHAIN_CONFIG.OPTIMISM.chainId] +// .fund_requirements.safe, +// }), +// {}, +// ); + +// return Object.values(fundRequirements).every((f) => f); +// }; + +export const useServices = () => { + const { + services, + isFetched: isLoaded, + paused, + setPaused: setServicesPollingPaused, + } = useContext(ServicesContext); - // STATE METHODS - const getServiceFromState = ( - serviceHash: ServiceHash, - ): MiddlewareServiceResponse | undefined => { - if (!hasInitialLoaded) return; + const getServicesByChain = ( + chainId: ChainId, + ): MiddlewareServiceResponse[] | undefined => { + if (!isLoaded) return; if (!services) return; - return services.find((service) => service.hash === serviceHash); + return services.filter( + (service) => service.chain_configs[chainId] !== undefined, + ); }; return { - // service: services?.[0], services, - serviceId, - serviceStatus, - setServiceStatus, - getServiceFromState, - getServicesFromState, - checkServiceIsFunded, - updateServicesState, - updateServiceState, - updateServiceStatus, - deleteServiceState, - hasInitialLoaded, - setIsServicePollingPaused: setIsPaused, + getServicesByChain, + isLoaded, + setServicesPollingPaused, + paused, }; }; From ac5fa34606777e2604e2b08d13015e445b8a60d9 Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 09:46:42 +0000 Subject: [PATCH 02/28] feat: number formatters --- frontend/utils/numberFormatters.ts | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/frontend/utils/numberFormatters.ts b/frontend/utils/numberFormatters.ts index 7613469f..1fe86ca2 100644 --- a/frontend/utils/numberFormatters.ts +++ b/frontend/utils/numberFormatters.ts @@ -1,3 +1,8 @@ +import { BigNumberish, ethers } from 'ethers'; + +/** + * Displays balance in a human readable format + */ export const balanceFormat = ( balance: number | undefined, decimals: 2, @@ -9,3 +14,33 @@ export const balanceFormat = ( minimumFractionDigits: decimals, }).format(balance); }; + +/** + * Formats larger numbers into small numbers + * i.e. wei to ether `formatUnits('1000000000000000000', 18)` => '1.0' + */ +export const formatUnits = (value: BigNumberish, decimals = 18): string => { + return ethers.utils.formatUnits(value, decimals); +}; + +/** + * Assumes the input is in wei and converts it to ether + */ +export const formatEther = (wei: BigNumberish): string => { + return ethers.utils.formatEther(wei); +}; + +/** + * Parse converts smaller numbers into larger numbers + * @example parseUnits('1.0', 18) => '1000000000000000000' + */ +export const parseUnits = (value: string, decimals: 18): string => { + return ethers.utils.parseUnits(value, decimals).toString(); +}; + +/** + * Assumes the input is in ether and converts it to wei + */ +export const parseEther = (ether: string | number | bigint): string => { + return ethers.utils.parseEther(`${ether}`).toString(); +}; From 8cc5565a980ac58ab557c817a8c4b68f4e87d47f Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 09:47:04 +0000 Subject: [PATCH 03/28] feat: extend ServiceResponse with service_config_id mock --- frontend/client/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/client/types.ts b/frontend/client/types.ts index e461348d..bb38ced5 100644 --- a/frontend/client/types.ts +++ b/frontend/client/types.ts @@ -41,6 +41,7 @@ export type ChainData = { }; export type MiddlewareServiceResponse = { + service_config_id: string; // TODO: update with uuid once middleware integrated name: string; hash: string; keys: ServiceKeys[]; From 05c984e5490732e8a52d7cf0b428316bf3967152 Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 09:57:05 +0000 Subject: [PATCH 04/28] feat: memoize services by chain record --- frontend/hooks/useServices.ts | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/frontend/hooks/useServices.ts b/frontend/hooks/useServices.ts index da199297..7c36c562 100644 --- a/frontend/hooks/useServices.ts +++ b/frontend/hooks/useServices.ts @@ -1,4 +1,4 @@ -import { useContext } from 'react'; +import { useContext, useMemo } from 'react'; import { MiddlewareServiceResponse } from '@/client'; import { ServicesContext } from '@/context/ServicesProvider'; @@ -83,24 +83,37 @@ export const useServices = () => { services, isFetched: isLoaded, paused, - setPaused: setServicesPollingPaused, + setPaused: setPaused, + selectedService, + selectService, } = useContext(ServicesContext); - const getServicesByChain = ( - chainId: ChainId, - ): MiddlewareServiceResponse[] | undefined => { + const servicesByChain = useMemo(() => { if (!isLoaded) return; if (!services) return; - return services.filter( - (service) => service.chain_configs[chainId] !== undefined, + return Object.keys(ChainId).reduce( + ( + acc: Record, + chainIdKey: string, + ) => { + const chainIdNumber = +chainIdKey; + acc[chainIdNumber] = services.filter( + (service: MiddlewareServiceResponse) => + service.chain_configs[chainIdNumber], + ); + return acc; + }, + {}, ); - }; + }, [isLoaded, services]); return { services, - getServicesByChain, + servicesByChain, isLoaded, - setServicesPollingPaused, + setPaused, paused, + selectedService, + selectService, }; }; From ecb7a301452838071697a4d70dc0e51bc1dab732 Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 09:57:18 +0000 Subject: [PATCH 05/28] refactor: single service hook updated --- frontend/hooks/useService.ts | 93 ++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 41 deletions(-) diff --git a/frontend/hooks/useService.ts b/frontend/hooks/useService.ts index 8e4cde0f..1ab6b48d 100644 --- a/frontend/hooks/useService.ts +++ b/frontend/hooks/useService.ts @@ -1,53 +1,64 @@ import { useMemo } from 'react'; +import { MiddlewareDeploymentStatus } from '@/client'; import { Address } from '@/types/Address'; -import { Service } from '@/types/Service'; + +import { useServices } from './useServices'; + +type ServiceChainIdAddressRecord = { + [chainId: number]: { + agentSafe?: Address; + agentEoas?: Address[]; + }; +}; /** * Hook for interacting with a single service. */ -export const useService = (service: Service) => { - // ChainIds used by the service - const chainIdsUsed = useMemo(() => { - return Object.keys(service.chain_configs).map(Number); - }, [service.chain_configs]); - - const addresses = useMemo<{ - [chainId: number]: { - master: { - safe: Address; - signer: Address; - }; - agent: { - safe: Address; - signer: Address; +export const useService = ({ + serviceConfigId, +}: { + serviceConfigId: string; +}) => { + const { services, isLoaded } = useServices(); + + const service = useMemo(() => { + return services?.find( + (service) => service.service_config_id === serviceConfigId, + ); + }, [serviceConfigId, services]); + + const addresses: ServiceChainIdAddressRecord = useMemo(() => { + if (!service) return {}; + const chainData = service.chain_configs; + + // group multisigs by chainId + const addressesByChainId: ServiceChainIdAddressRecord = Object.keys( + chainData, + ).reduce((acc, chainIdKey) => { + const chainId = +chainIdKey; + + const chain = chainData[chainId]; + if (!chain) return acc; + + const { multisig, instances } = chain.chain_data; + + return { + ...acc, + [chainId]: { + agentSafe: multisig, + agentEoas: instances, + }, }; - }; - }>( - () => - chainIdsUsed.reduce((acc, chainId) => { - const chainConfig = service.chain_configs[chainId]; - const master = { - safe: chainConfig.master.safe, - signer: chainConfig.master.signer, - }; - const agent = { - safe: chainConfig.agent.safe, - signer: chainConfig.agent.signer, - }; - - return { - ...acc, - [chainId]: { - master, - agent, - }, - }; - }, {}), - [chainIdsUsed, service.chain_configs], - ); + }, {}); + + return addressesByChainId; + }, [service]); return { - wallets, + service, + addresses, + serviceStatus: MiddlewareDeploymentStatus.DEPLOYED, // TODO support other statuses + isLoaded, }; }; From 4de127df05c6b40e221b7a6b94774dc22cd63723 Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 10:23:01 +0000 Subject: [PATCH 06/28] refactor: split agent buttons, implement updated services hooks --- .../header/AgentButton/AgentButton.tsx | 61 ++++++ .../AgentNotRunningButton.tsx} | 200 +++--------------- .../header/AgentButton/AgentRunningButton.tsx | 83 ++++++++ .../AgentButton/AgentStartingButton.tsx | 27 +++ .../AgentButton/AgentStoppingButton.tsx | 7 + 5 files changed, 209 insertions(+), 169 deletions(-) create mode 100644 frontend/components/MainPage/header/AgentButton/AgentButton.tsx rename frontend/components/MainPage/header/{AgentButton.tsx => AgentButton/AgentNotRunningButton.tsx} (55%) create mode 100644 frontend/components/MainPage/header/AgentButton/AgentRunningButton.tsx create mode 100644 frontend/components/MainPage/header/AgentButton/AgentStartingButton.tsx create mode 100644 frontend/components/MainPage/header/AgentButton/AgentStoppingButton.tsx diff --git a/frontend/components/MainPage/header/AgentButton/AgentButton.tsx b/frontend/components/MainPage/header/AgentButton/AgentButton.tsx new file mode 100644 index 00000000..11b3a4cf --- /dev/null +++ b/frontend/components/MainPage/header/AgentButton/AgentButton.tsx @@ -0,0 +1,61 @@ +import { Button } from 'antd'; +import { useMemo } from 'react'; + +import { MiddlewareDeploymentStatus } from '@/client'; +import { useService } from '@/hooks/useService'; +import { useServices } from '@/hooks/useServices'; +import { useStakingContractInfo } from '@/hooks/useStakingContractInfo'; + +import { + CannotStartAgentDueToUnexpectedError, + CannotStartAgentPopover, +} from '../CannotStartAgentPopover'; +import { AgentNotRunningButton } from './AgentNotRunningButton'; +import { AgentRunningButton } from './AgentRunningButton'; +import { AgentStartingButton } from './AgentStartingButton'; +import { AgentStoppingButton } from './AgentStoppingButton'; + +export const AgentButton = () => { + const { selectedService } = useServices(); + + const { + service, + deploymentStatus: serviceStatus, + isLoaded, + } = useService({ serviceConfigId: selectedService?.service_config_id }); + + const { isEligibleForStaking, isAgentEvicted } = useStakingContractInfo(); + + return useMemo(() => { + if (!isLoaded) { + return - -); - -const AgentStoppingButton = () => ( - -); - -const AgentRunningButton = () => { - const { showNotification } = useElectronApi(); - const { isEligibleForRewards } = useReward(); - const { service, setIsServicePollingPaused, setServiceStatus } = - useServices(); - - const handlePause = useCallback(async () => { - if (!service) return; - // Paused to stop overlapping service poll while waiting for response - setIsServicePollingPaused(true); - - // Optimistically update service status - setServiceStatus(MiddlewareDeploymentStatus.STOPPING); - try { - await ServicesService.stopDeployment(service.hash); - } catch (error) { - console.error(error); - showNotification?.('Error while stopping agent'); - } finally { - // Resume polling, will update to correct status regardless of success - setIsServicePollingPaused(false); - } - }, [service, setIsServicePollingPaused, setServiceStatus, showNotification]); - - return ( - - - - - {isEligibleForRewards ? ( - - Agent is idle  - - - ) : ( - - Agent is working - - )} - - - - ); -}; +import { requiredGas } from '../constants'; /** Button used to start / deploy the agent */ -const AgentNotRunningButton = () => { +export const AgentNotRunningButton = () => { const { wallets, masterSafeAddress } = useWallet(); + const { - service, - serviceStatus, - setServiceStatus, - setIsServicePollingPaused, - updateServicesState, + selectedService, + setPaused: setIsServicePollingPaused, + isLoaded, + refetch: updateServicesState, } = useServices(); + + const { service, deploymentStatus, setDeploymentStatus } = useService({ + serviceConfigId: + isLoaded && selectedService ? selectedService?.service_config_id : '', + }); + const { serviceTemplate } = useServiceTemplates(); const { showNotification } = useElectronApi(); const { @@ -146,16 +53,15 @@ const AgentNotRunningButton = () => { setIsPaused: setIsStakingContractInfoPollingPaused, updateActiveStakingContractInfo, } = useStakingContractInfo(); + const { activeStakingProgramId } = useStakingProgram(); // const minStakingDeposit = // stakingContractInfoRecord?.[activeStakingProgram ?? defaultStakingProgram] // ?.minStakingDeposit; - const requiredOlas = getMinimumStakedAmountRequired( - serviceTemplate, - activeStakingProgramId ?? DEFAULT_STAKING_PROGRAM_ID, - ); + const requiredOlas = + STAKING_PROGRAMS[activeStakingProgramId]?.minStakingDeposit; // TODO: fix activeStakingProgramId const safeOlasBalance = safeBalance?.OLAS; const safeOlasBalanceWithStaked = @@ -177,7 +83,7 @@ const AgentNotRunningButton = () => { setIsStakingContractInfoPollingPaused(true); // Mock "DEPLOYING" status (service polling will update this once resumed) - setServiceStatus(MiddlewareDeploymentStatus.DEPLOYING); + setDeploymentStatus(MiddlewareDeploymentStatus.DEPLOYING); // Get the active staking program id; default id if there's no agent yet const stakingProgramId: StakingProgramId = @@ -190,7 +96,7 @@ const AgentNotRunningButton = () => { } } catch (error) { console.error(error); - setServiceStatus(undefined); + setDeploymentStatus(undefined); showNotification?.('Error while creating safe'); setIsStakingContractInfoPollingPaused(false); setIsServicePollingPaused(false); @@ -208,7 +114,7 @@ const AgentNotRunningButton = () => { }); } catch (error) { console.error(error); - setServiceStatus(undefined); + setDeploymentStatus(undefined); showNotification?.('Error while deploying service'); setIsServicePollingPaused(false); setIsBalancePollingPaused(false); @@ -225,7 +131,7 @@ const AgentNotRunningButton = () => { } // Can assume successful deployment - setServiceStatus(MiddlewareDeploymentStatus.DEPLOYED); + setDeploymentStatus(MiddlewareDeploymentStatus.DEPLOYED); // TODO: remove this workaround, middleware should respond when agent is staked & confirmed running after `createService` call await delayInSeconds(5); @@ -233,7 +139,7 @@ const AgentNotRunningButton = () => { // update provider states sequentially // service id is required before activeStakingContractInfo & balances can be updated try { - await updateServicesState(); // reload the available services + await updateServicesState?.(); // reload the available services await updateActiveStakingContractInfo(); // reload active staking contract with new service await updateBalances(); // reload the balances } catch (error) { @@ -249,7 +155,7 @@ const AgentNotRunningButton = () => { setIsServicePollingPaused, setIsBalancePollingPaused, setIsStakingContractInfoPollingPaused, - setServiceStatus, + setDeploymentStatus, masterSafeAddress, showNotification, activeStakingProgramId, @@ -263,15 +169,15 @@ const AgentNotRunningButton = () => { // if the agent is NOT running and the balance is too low, // user should not be able to start the agent const isServiceInactive = - serviceStatus === MiddlewareDeploymentStatus.BUILT || - serviceStatus === MiddlewareDeploymentStatus.STOPPED; + deploymentStatus === MiddlewareDeploymentStatus.BUILT || + deploymentStatus === MiddlewareDeploymentStatus.STOPPED; if (isServiceInactive && isLowBalance) { return false; } - if (serviceStatus === MiddlewareDeploymentStatus.DEPLOYED) return false; - if (serviceStatus === MiddlewareDeploymentStatus.DEPLOYING) return false; - if (serviceStatus === MiddlewareDeploymentStatus.STOPPING) return false; + if (deploymentStatus === MiddlewareDeploymentStatus.DEPLOYED) return false; + if (deploymentStatus === MiddlewareDeploymentStatus.DEPLOYING) return false; + if (deploymentStatus === MiddlewareDeploymentStatus.STOPPING) return false; if (!requiredOlas) return false; @@ -290,7 +196,7 @@ const AgentNotRunningButton = () => { return hasEnoughOlas && hasEnoughEth; }, [ - serviceStatus, + deploymentStatus, service, storeState?.isInitialFunded, isEligibleForStaking, @@ -312,47 +218,3 @@ const AgentNotRunningButton = () => { return ; }; - -export const AgentButton = () => { - const { service, serviceStatus, hasInitialLoaded } = useServices(); - const { isEligibleForStaking, isAgentEvicted } = useStakingContractInfo(); - - return useMemo(() => { - if (!hasInitialLoaded) { - return + + + {isEligibleForRewards ? ( + + Agent is idle  + + + ) : ( + + Agent is working + + )} + + + + ); +}; diff --git a/frontend/components/MainPage/header/AgentButton/AgentStartingButton.tsx b/frontend/components/MainPage/header/AgentButton/AgentStartingButton.tsx new file mode 100644 index 00000000..6503f6cf --- /dev/null +++ b/frontend/components/MainPage/header/AgentButton/AgentStartingButton.tsx @@ -0,0 +1,27 @@ +import { InfoCircleOutlined } from '@ant-design/icons'; +import { Button, Flex, Popover, Typography } from 'antd'; + +import { COLOR } from '@/constants/colors'; + +const LOADING_MESSAGE = + "Starting the agent may take a while, so feel free to minimize the app. We'll notify you once it's running. Please, don't quit the app."; + +export const AgentStartingButton = () => ( + + + + + {LOADING_MESSAGE} + + } + > + + +); diff --git a/frontend/components/MainPage/header/AgentButton/AgentStoppingButton.tsx b/frontend/components/MainPage/header/AgentButton/AgentStoppingButton.tsx new file mode 100644 index 00000000..8fdea7f3 --- /dev/null +++ b/frontend/components/MainPage/header/AgentButton/AgentStoppingButton.tsx @@ -0,0 +1,7 @@ +import { Button } from 'antd'; + +export const AgentStoppingButton = () => ( + +); From 372c0fcf645e0ad8205a90f4ba46dbafd80f2179 Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 10:23:57 +0000 Subject: [PATCH 07/28] refactor: support updated services in useSetupTrayIcon --- frontend/components/MainPage/header/index.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/frontend/components/MainPage/header/index.tsx b/frontend/components/MainPage/header/index.tsx index e5948b57..786af663 100644 --- a/frontend/components/MainPage/header/index.tsx +++ b/frontend/components/MainPage/header/index.tsx @@ -4,28 +4,32 @@ import { useCallback, useEffect, useState } from 'react'; import { MiddlewareDeploymentStatus } from '@/client'; import { useBalance } from '@/hooks/useBalance'; import { useElectronApi } from '@/hooks/useElectronApi'; +import { useService } from '@/hooks/useService'; import { useServices } from '@/hooks/useServices'; import { FirstRunModal } from '../modals/FirstRunModal'; -import { AgentButton } from './AgentButton'; +import { AgentButton } from './AgentButton/AgentButton'; import { AgentHead } from './AgentHead'; const useSetupTrayIcon = () => { const { isLowBalance } = useBalance(); - const { serviceStatus } = useServices(); + const { selectedService } = useServices(); + const { deploymentStatus } = useService({ + serviceConfigId: selectedService?.service_config_id, + }); const { setTrayIcon } = useElectronApi(); useEffect(() => { if (isLowBalance) { setTrayIcon?.('low-gas'); - } else if (serviceStatus === MiddlewareDeploymentStatus.DEPLOYED) { + } else if (deploymentStatus === MiddlewareDeploymentStatus.DEPLOYED) { setTrayIcon?.('running'); - } else if (serviceStatus === MiddlewareDeploymentStatus.STOPPED) { + } else if (deploymentStatus === MiddlewareDeploymentStatus.STOPPED) { setTrayIcon?.('paused'); - } else if (serviceStatus === MiddlewareDeploymentStatus.BUILT) { + } else if (deploymentStatus === MiddlewareDeploymentStatus.BUILT) { setTrayIcon?.('logged-out'); } - }, [isLowBalance, serviceStatus, setTrayIcon]); + }, [isLowBalance, deploymentStatus, setTrayIcon]); return null; }; From ded9a09d92a91072a3a66ce657f4600327a7cb74 Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 10:35:50 +0000 Subject: [PATCH 08/28] refactor: migrate button to support new service hooks --- .../StakingContractSection/MigrateButton.tsx | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/frontend/components/ManageStakingPage/StakingContractSection/MigrateButton.tsx b/frontend/components/ManageStakingPage/StakingContractSection/MigrateButton.tsx index f2589d32..4aa288b4 100644 --- a/frontend/components/ManageStakingPage/StakingContractSection/MigrateButton.tsx +++ b/frontend/components/ManageStakingPage/StakingContractSection/MigrateButton.tsx @@ -8,6 +8,7 @@ import { StakingProgramId } from '@/enums/StakingProgram'; import { useBalance } from '@/hooks/useBalance'; import { useModals } from '@/hooks/useModals'; import { usePageState } from '@/hooks/usePageState'; +import { useService } from '@/hooks/useService'; import { useServices } from '@/hooks/useServices'; import { useServiceTemplates } from '@/hooks/useServiceTemplates'; import { useStakingContractInfo } from '@/hooks/useStakingContractInfo'; @@ -20,32 +21,41 @@ import { CantMigrateReason, useMigrate } from './useMigrate'; type MigrateButtonProps = { stakingProgramId: StakingProgramId; }; -export const MigrateButton = ({ stakingProgramId }: MigrateButtonProps) => { +export const MigrateButton = ({ + stakingProgramId: stakingProgramIdToMigrateTo, +}: MigrateButtonProps) => { const { goto } = usePageState(); const { serviceTemplate } = useServiceTemplates(); const { - setIsServicePollingPaused, - setServiceStatus, - updateServiceStatus, - hasInitialLoaded: isServicesLoaded, - service, + setPaused: setIsServicePollingPaused, + isLoaded: isServicesLoaded, + selectedService, } = useServices(); + + const { setDeploymentStatus } = useService({ + serviceConfigId: + isServicesLoaded && selectedService + ? selectedService.service_config_id + : '', + }); + const { setIsPaused: setIsBalancePollingPaused } = useBalance(); const { updateActiveStakingProgramId: updateStakingProgram } = useStakingProgram(); const { activeStakingContractInfo } = useStakingContractInfo(); const { setMigrationModalOpen } = useModals(); - const { migrateValidation, firstDeployValidation } = - useMigrate(stakingProgramId); + const { migrateValidation, firstDeployValidation } = useMigrate( + stakingProgramIdToMigrateTo, + ); // if false, user is migrating, not running for first time const isFirstDeploy = useMemo(() => { if (!isServicesLoaded) return false; - if (service) return false; + if (selectedService) return false; return true; - }, [isServicesLoaded, service]); + }, [isServicesLoaded, selectedService]); const validation = isFirstDeploy ? firstDeployValidation : migrateValidation; @@ -77,17 +87,19 @@ export const MigrateButton = ({ stakingProgramId }: MigrateButtonProps) => { setIsBalancePollingPaused(true); try { - setServiceStatus(MiddlewareDeploymentStatus.DEPLOYING); + setDeploymentStatus(MiddlewareDeploymentStatus.DEPLOYING); goto(Pages.Main); - await ServicesService.createService({ - stakingProgramId, + // TODO: create type for this response, we need the service_config_id to update the relevant service + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const createServiceResponse = await ServicesService.createService({ + stakingProgramId: stakingProgramIdToMigrateTo, serviceTemplate, deploy: true, useMechMarketplace: false, }); - await updateStakingProgram(); + await updateStakingProgram(); // TODO: refactor to support single staking program & multi staking programs, this on longer works setMigrationModalOpen(true); } catch (error) { @@ -95,7 +107,7 @@ export const MigrateButton = ({ stakingProgramId }: MigrateButtonProps) => { } finally { setIsServicePollingPaused(false); setIsBalancePollingPaused(false); - updateServiceStatus(); + // updateServiceStatus(); // TODO: update service status } }} > From 2870ba67fa11d19e79e2271aff3be6572ef0a0ec Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 10:36:26 +0000 Subject: [PATCH 09/28] chore: add todo --- .../ManageStakingPage/StakingContractSection/useMigrate.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/components/ManageStakingPage/StakingContractSection/useMigrate.tsx b/frontend/components/ManageStakingPage/StakingContractSection/useMigrate.tsx index 2f4dde07..33c6507a 100644 --- a/frontend/components/ManageStakingPage/StakingContractSection/useMigrate.tsx +++ b/frontend/components/ManageStakingPage/StakingContractSection/useMigrate.tsx @@ -53,10 +53,10 @@ export const useMigrate = (stakingProgramId: StakingProgramId) => { const stakingContractInfo = stakingContractInfoRecord?.[stakingProgramId]; - const { hasInitialLoaded: isServicesLoaded } = useServices(); + const { isLoaded: isServicesLoaded } = useServices(); const minimumOlasRequiredToMigrate = useMemo( - () => getMinimumStakedAmountRequired(serviceTemplate, stakingProgramId), + () => getMinimumStakedAmountRequired(serviceTemplate, stakingProgramId), // TODO: refactor, can no longer use service template, must use config for funding requirements [serviceTemplate, stakingProgramId], ); From 75c039d7378fea7500b8b5211421290baa8549db Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 10:37:42 +0000 Subject: [PATCH 10/28] refactor: comment optimus, add todos --- frontend/config/agents.ts | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/frontend/config/agents.ts b/frontend/config/agents.ts index d387c577..db97cbf2 100644 --- a/frontend/config/agents.ts +++ b/frontend/config/agents.ts @@ -1,21 +1,26 @@ import { AgentType } from '@/enums/Agent'; import { ChainId } from '@/enums/Chain'; -import { OptimusService } from '@/service/agents/Optimus'; import { PredictTraderService } from '@/service/agents/PredictTrader'; +// TODO: complete this config +// TODO: add funding requirements + export const AGENT_CONFIG = { [AgentType.PredictTrader]: { name: 'Predict Trader', homeChainId: ChainId.Gnosis, requiresAgentSafesOn: [ChainId.Gnosis], + agentSafeFundingRequirements: { + [ChainId.Gnosis]: 100000000000000000, + }, requiresMasterSafesOn: [ChainId.Gnosis], serviceApi: PredictTraderService, }, - [AgentType.Optimus]: { - name: 'Optimus', - homeChainId: ChainId.Optimism, - requiresAgentSafesOn: [ChainId.Optimism, ChainId.Ethereum, ChainId.Base], - requiresMasterSafesOn: [ChainId.Optimism, ChainId.Ethereum, ChainId.Base], - serviceApi: OptimusService, - }, + // [AgentType.Optimus]: { + // name: 'Optimus', + // homeChainId: ChainId.Optimism, + // requiresAgentSafesOn: [ChainId.Optimism, ChainId.Ethereum, ChainId.Base], + // requiresMasterSafesOn: [ChainId.Optimism, ChainId.Ethereum, ChainId.Base], + // serviceApi: OptimusService, + // }, }; From 68ee3dc256d6a67cb94c0eefbf5f2f93df36431c Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 10:46:29 +0000 Subject: [PATCH 11/28] refactor: add todos for future improvements and streamline balance fetching logic --- frontend/context/BalanceProvider.tsx | 59 +++++++++++++--------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/frontend/context/BalanceProvider.tsx b/frontend/context/BalanceProvider.tsx index eae56b74..c3fd0da4 100644 --- a/frontend/context/BalanceProvider.tsx +++ b/frontend/context/BalanceProvider.tsx @@ -1,6 +1,7 @@ +// TODO: large refactor needed, provider is way too big, needs to support multiple chains, multiple services, and multiple tokens import { message } from 'antd'; import { isAddress } from 'ethers/lib/utils'; -import { isNumber } from 'lodash'; +import { isNil, isNumber } from 'lodash'; import { ValueOf } from 'next/dist/shared/lib/constants'; import { createContext, @@ -22,9 +23,10 @@ import { LOW_AGENT_SAFE_BALANCE, LOW_MASTER_SAFE_BALANCE, } from '@/constants/thresholds'; +import { ChainId } from '@/enums/Chain'; import { ServiceRegistryL2ServiceState } from '@/enums/ServiceRegistryL2ServiceState'; import { TokenSymbol } from '@/enums/Token'; -import { AutonolasService } from '@/service/Autonolas'; +import { StakedAgentService } from '@/service/agents/StakedAgentService'; import { EthersService } from '@/service/Ethers'; import MulticallService from '@/service/Multicall'; import { Address } from '@/types/Address'; @@ -97,10 +99,8 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { const [isBalanceLoaded, setIsBalanceLoaded] = useState(false); const [walletBalances, setWalletBalances] = useState({}); - const [baseBalance, setBaseBalance] = useState(); - const [ethereumBalance, setEthereumBalance] = useState(); - const [optimismBalance, setOptimismBalance] = useState(); + // TODO: refactor to support multiple chains, and gas tokens from config const totalEthBalance: number | undefined = useMemo(() => { if (!isLoaded) return; return Object.values(walletBalances).reduce( @@ -152,28 +152,16 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { } // fetch balances for other chains - try { - const baseBalanceTemp = - EthersService.getBaseBalance(masterEoaAddress).then(setBaseBalance); - - const ethereumBalanceTemp = - EthersService.getEthereumBalance(masterEoaAddress).then( - setEthereumBalance, - ); - - const optimismBalanceTemp = - EthersService.getOptimismBalance(masterEoaAddress).then( - setOptimismBalance, - ); - - await Promise.allSettled([ - baseBalanceTemp, - ethereumBalanceTemp, - optimismBalanceTemp, - ]); - } catch (error) { - console.error(error); - } + // TODO: refactor to dynamically fetch balances for all chains + // try { + // await Promise.allSettled([ + // baseBalanceTemp, + // ethereumBalanceTemp, + // optimismBalanceTemp, + // ]); + // } catch (error) { + // console.error(error); + // } try { const walletBalances = await getWalletBalances(walletAddresses); @@ -181,6 +169,7 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { setWalletBalances(walletBalances); + // TODO: refactor to use ChainId enum, service from useService(), const serviceId = services?.[0]?.chain_configs[CHAIN_CONFIG.OPTIMISM.chainId].chain_data .token; @@ -191,11 +180,16 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { return; } - if (isAddress(`${masterSafeAddress}`) && serviceId > 0) { + if ( + !isNil(masterSafeAddress) && + isAddress(masterSafeAddress) && + serviceId > 0 + ) { const { depositValue, bondValue, serviceState } = - await AutonolasService.getServiceRegistryInfo( - masterSafeAddress as Address, + await StakedAgentService.getServiceRegistryInfo( + masterSafeAddress, serviceId, + ChainId.Gnosis, // TODO: refactor to get chain id from service ); switch (serviceState) { @@ -234,7 +228,7 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { message.error('Unable to retrieve wallet balances'); setIsBalanceLoaded(true); } - }, [masterEoaAddress, masterSafeAddress, serviceAddresses, services]); + }, [masterEoaAddress, masterSafeAddress, services]); const agentEoaAddress = useMemo( () => @@ -242,14 +236,17 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { ?.instances?.[0], [services], ); + const masterEoaBalance = useMemo( () => masterEoaAddress && walletBalances[masterEoaAddress], [masterEoaAddress, walletBalances], ); + const masterSafeBalance = useMemo( () => masterSafeAddress && walletBalances[masterSafeAddress], [masterSafeAddress, walletBalances], ); + const agentSafeBalance = useMemo( () => services?.[0]?.chain_configs[CHAIN_CONFIG.OPTIMISM.chainId].chain_data From d9800bc24ba9feda94bc149d702e521d5230db60 Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 10:49:28 +0000 Subject: [PATCH 12/28] refactor: rename selectedServiceUuid to selectedServiceConfigId and update related logic --- frontend/context/ServicesProvider.tsx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/frontend/context/ServicesProvider.tsx b/frontend/context/ServicesProvider.tsx index 8eec40b9..38a3cafc 100644 --- a/frontend/context/ServicesProvider.tsx +++ b/frontend/context/ServicesProvider.tsx @@ -41,7 +41,8 @@ export const ServicesProvider = ({ children }: PropsWithChildren) => { const { paused, setPaused, togglePaused } = usePause(); // user selected service identifier - const [selectedServiceUuid, setSelectedServiceUuid] = useState(); + const [selectedServiceConfigId, setSelectedServiceConfigId] = + useState(); const { data: services, @@ -59,17 +60,23 @@ export const ServicesProvider = ({ children }: PropsWithChildren) => { const selectedService = useMemo(() => { if (!services) return; - return services.find((service) => service.hash === selectedServiceUuid); // TODO: use uuid instead of hash once middleware refactored - }, [selectedServiceUuid, services]); + return services.find((service) => service.hash === selectedServiceConfigId); // TODO: use uuid instead of hash once middleware refactored + }, [selectedServiceConfigId, services]); const selectService = useCallback((serviceUuid: string) => { - setSelectedServiceUuid(serviceUuid); + setSelectedServiceConfigId(serviceUuid); }, []); + /** + * Select the first service by default + */ useEffect(() => { if (!services) return; - setSelectedServiceUuid(services[0]?.hash); // TODO: use uuid instead of hash once middleware refactored - }, [services]); + if (selectedServiceConfigId) return; + // only select a service by default if services are fetched, but there has been no selection yet + if (isFetched && services.length > 0 && !selectedServiceConfigId) + setSelectedServiceConfigId(services[0].service_config_id); // TODO: use uuid instead of hash once middleware refactored + }, [isFetched, selectedServiceConfigId, services]); // const serviceAddresses = useMemo( // () => From bc9c0db541b2814b9e8ec7912031a4b362495418 Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 10:50:28 +0000 Subject: [PATCH 13/28] feat: add wallet enums, easier to distinguish between what a wallet is, what entity owns it, and enforce types to stop conflicts i.e. running safe functions on an eoa --- frontend/enums/Wallet.ts | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 frontend/enums/Wallet.ts diff --git a/frontend/enums/Wallet.ts b/frontend/enums/Wallet.ts new file mode 100644 index 00000000..f6496c1d --- /dev/null +++ b/frontend/enums/Wallet.ts @@ -0,0 +1,9 @@ +export enum WalletType { + Safe = 'multisig', + EOA = 'eoa', +} + +export enum WalletOwner { + Master = 'master', // user + Agent = 'agent', +} From a777e7c74f3e8d9795e39bbe1c9dc4d2b9510fc4 Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 10:56:30 +0000 Subject: [PATCH 14/28] refactor: support multiple services & statuses in useLogs --- frontend/hooks/useLogs.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/frontend/hooks/useLogs.ts b/frontend/hooks/useLogs.ts index c024fc43..45e1b93f 100644 --- a/frontend/hooks/useLogs.ts +++ b/frontend/hooks/useLogs.ts @@ -1,6 +1,7 @@ +import { useQueryClient } from '@tanstack/react-query'; import { useMemo } from 'react'; -import { MiddlewareDeploymentStatus } from '@/client'; +import { REACT_QUERY_KEYS } from '@/constants/react-query-keys'; import { useBalance } from './useBalance'; import { useMasterSafe } from './useMasterSafe'; @@ -46,19 +47,24 @@ const useBalancesLogs = () => { }; }; +// TODO: refactor to support logs for multiple services const useServicesLogs = () => { - const { serviceStatus, services, hasInitialLoaded } = useServices(); + const { services, isLoaded } = useServices(); + const { getQueryData } = useQueryClient(); return { - isLoaded: hasInitialLoaded, + isLoaded: isLoaded, data: { - serviceStatus: serviceStatus - ? MiddlewareDeploymentStatus[serviceStatus] - : 'undefined', services: services?.map((item) => ({ ...item, keys: item.keys.map((key) => key.address), + deploymentStatus: getQueryData([ + REACT_QUERY_KEYS.SERVICE_DEPLOYMENT_STATUS_KEY( + item.service_config_id, + ), + item.service_config_id, + ]), })) ?? 'undefined', }, }; From 2345c457ed7175bdbe8a35c8b9f36bad5d656077 Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 10:56:47 +0000 Subject: [PATCH 15/28] chore: rename keys to have KEY suffix, easier to understand --- frontend/constants/react-query-keys.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/constants/react-query-keys.ts b/frontend/constants/react-query-keys.ts index 8c7cb031..1d509171 100644 --- a/frontend/constants/react-query-keys.ts +++ b/frontend/constants/react-query-keys.ts @@ -1,4 +1,5 @@ export const REACT_QUERY_KEYS = { - SERVICES: ['services'] as const, - SERVICE_STATUS: (uuid: string) => ['serviceStatus', uuid] as const, + SERVICES_KEY: ['services'] as const, + SERVICE_DEPLOYMENT_STATUS_KEY: (serviceConfigId: string) => + ['serviceStatus', serviceConfigId] as const, } as const; From 98a17432d4911db0eb25a1b7ec71425537b6e00e Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 10:57:01 +0000 Subject: [PATCH 16/28] chore: use renamed react query key --- frontend/context/ServicesProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/context/ServicesProvider.tsx b/frontend/context/ServicesProvider.tsx index 38a3cafc..8ccdb3e4 100644 --- a/frontend/context/ServicesProvider.tsx +++ b/frontend/context/ServicesProvider.tsx @@ -52,7 +52,7 @@ export const ServicesProvider = ({ children }: PropsWithChildren) => { isFetching, refetch, } = useQuery({ - queryKey: REACT_QUERY_KEYS.SERVICES, + queryKey: REACT_QUERY_KEYS.SERVICES_KEY, queryFn: ServicesService.getServices, enabled: isOnline && !paused, refetchInterval: FIVE_SECONDS_INTERVAL, From 902a3b2f7939538637b907da2566cf4f0a40a6a2 Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 10:57:30 +0000 Subject: [PATCH 17/28] feat: support overwriting service deployment status in reactquery cache --- frontend/hooks/useService.ts | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/frontend/hooks/useService.ts b/frontend/hooks/useService.ts index 1ab6b48d..b02fcae7 100644 --- a/frontend/hooks/useService.ts +++ b/frontend/hooks/useService.ts @@ -1,6 +1,8 @@ +import { useQueryClient } from '@tanstack/react-query'; import { useMemo } from 'react'; import { MiddlewareDeploymentStatus } from '@/client'; +import { REACT_QUERY_KEYS } from '@/constants/react-query-keys'; import { Address } from '@/types/Address'; import { useServices } from './useServices'; @@ -16,11 +18,12 @@ type ServiceChainIdAddressRecord = { * Hook for interacting with a single service. */ export const useService = ({ - serviceConfigId, + serviceConfigId = '', }: { - serviceConfigId: string; + serviceConfigId?: string; }) => { const { services, isLoaded } = useServices(); + const queryClient = useQueryClient(); const service = useMemo(() => { return services?.find( @@ -55,10 +58,25 @@ export const useService = ({ return addressesByChainId; }, [service]); + /** + * Overrides the deployment status of the service in the cache. + * @note Overwrite is only temporary if ServicesContext is polling + */ + const setDeploymentStatus = (deploymentStatus?: MiddlewareDeploymentStatus) => + queryClient.setQueryData( + REACT_QUERY_KEYS.SERVICE_DEPLOYMENT_STATUS_KEY(serviceConfigId), + deploymentStatus, + ); + + const deploymentStatus = queryClient.getQueryData< + MiddlewareDeploymentStatus | undefined + >(REACT_QUERY_KEYS.SERVICE_DEPLOYMENT_STATUS_KEY(serviceConfigId)); + return { service, addresses, - serviceStatus: MiddlewareDeploymentStatus.DEPLOYED, // TODO support other statuses isLoaded, + deploymentStatus, // TODO support other statuses + setDeploymentStatus, }; }; From ec7ea8e5a6a4939cb34527d745862b7dd67ef7a9 Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 10:57:42 +0000 Subject: [PATCH 18/28] chore: export services context refetch --- frontend/hooks/useServices.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/hooks/useServices.ts b/frontend/hooks/useServices.ts index 7c36c562..92967577 100644 --- a/frontend/hooks/useServices.ts +++ b/frontend/hooks/useServices.ts @@ -86,6 +86,7 @@ export const useServices = () => { setPaused: setPaused, selectedService, selectService, + refetch, } = useContext(ServicesContext); const servicesByChain = useMemo(() => { @@ -115,5 +116,6 @@ export const useServices = () => { paused, selectedService, selectService, + refetch, }; }; From dd9c8d0efb6f47fe0c8d3333f74db38d0d6ed5ef Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 11:19:29 +0000 Subject: [PATCH 19/28] chore: import Address type from local types --- frontend/service/agents/StakedAgentService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/service/agents/StakedAgentService.ts b/frontend/service/agents/StakedAgentService.ts index 837df5d2..a10a34cf 100644 --- a/frontend/service/agents/StakedAgentService.ts +++ b/frontend/service/agents/StakedAgentService.ts @@ -7,7 +7,6 @@ * @note `noop` functions should be replaced with actual implementations in the extending classes. * @warning DO NOT STORE STATE IN THESE CLASSES. THEY ARE SINGLETONS AND WILL BE SHARED ACROSS THE APPLICATION. */ -import { Address } from 'cluster'; import { ethers } from 'ethers'; import { Contract as MulticallContract } from 'ethers-multicall'; @@ -18,6 +17,7 @@ import { ChainId } from '@/enums/Chain'; import { ContractType } from '@/enums/Contract'; import { ServiceRegistryL2ServiceState } from '@/enums/ServiceRegistryL2ServiceState'; import { StakingProgramId } from '@/enums/StakingProgram'; +import { Address } from '@/types/Address'; export const ONE_YEAR = 1 * 24 * 60 * 60 * 365; From 2eddecc67366a7f8dd8686ba2a5a409ef159ff98 Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 11:41:51 +0000 Subject: [PATCH 20/28] chore: remove todo comment for useLogs refactor --- frontend/hooks/useLogs.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/hooks/useLogs.ts b/frontend/hooks/useLogs.ts index 45e1b93f..a6155dab 100644 --- a/frontend/hooks/useLogs.ts +++ b/frontend/hooks/useLogs.ts @@ -47,7 +47,6 @@ const useBalancesLogs = () => { }; }; -// TODO: refactor to support logs for multiple services const useServicesLogs = () => { const { services, isLoaded } = useServices(); const { getQueryData } = useQueryClient(); From c32d9c5212b4b2e4a1e504765e2ea9643aaa8543 Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 11:49:28 +0000 Subject: [PATCH 21/28] refactor: improve number formatting utility functions and enhance documentation --- frontend/utils/numberFormatters.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/frontend/utils/numberFormatters.ts b/frontend/utils/numberFormatters.ts index 1fe86ca2..ceb28dd9 100644 --- a/frontend/utils/numberFormatters.ts +++ b/frontend/utils/numberFormatters.ts @@ -17,7 +17,8 @@ export const balanceFormat = ( /** * Formats larger numbers into small numbers - * i.e. wei to ether `formatUnits('1000000000000000000', 18)` => '1.0' + * @note **divides** the input by 10^decimals + * @example `formatUnits('1000000000000000000', 18)` => '1.0' */ export const formatUnits = (value: BigNumberish, decimals = 18): string => { return ethers.utils.formatUnits(value, decimals); @@ -31,16 +32,20 @@ export const formatEther = (wei: BigNumberish): string => { }; /** - * Parse converts smaller numbers into larger numbers + * Parse converts small numbers into larger numbers + * @note **multiplies** the input by `10 ** decimals` * @example parseUnits('1.0', 18) => '1000000000000000000' */ -export const parseUnits = (value: string, decimals: 18): string => { - return ethers.utils.parseUnits(value, decimals).toString(); +export const parseUnits = ( + value: BigNumberish, + decimals: number = 18, +): string => { + return ethers.utils.parseUnits(`${value}`, decimals).toString(); }; /** * Assumes the input is in ether and converts it to wei */ -export const parseEther = (ether: string | number | bigint): string => { +export const parseEther = (ether: BigNumberish): string => { return ethers.utils.parseEther(`${ether}`).toString(); }; From 0967f32742bf1bdd7dd8262baf1fb936a96397c7 Mon Sep 17 00:00:00 2001 From: truemiller Date: Tue, 12 Nov 2024 11:49:39 +0000 Subject: [PATCH 22/28] chore: remove todo comment for deploymentStatus in useService hook --- frontend/hooks/useService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/hooks/useService.ts b/frontend/hooks/useService.ts index b02fcae7..e4a82708 100644 --- a/frontend/hooks/useService.ts +++ b/frontend/hooks/useService.ts @@ -76,7 +76,7 @@ export const useService = ({ service, addresses, isLoaded, - deploymentStatus, // TODO support other statuses + deploymentStatus, setDeploymentStatus, }; }; From 9ef2eec340a3909aaf6b93e8d1ced437237d249d Mon Sep 17 00:00:00 2001 From: Josh Miller <31908788+truemiller@users.noreply.github.com> Date: Tue, 12 Nov 2024 11:53:31 +0000 Subject: [PATCH 23/28] chore: remove todo --- frontend/context/ServicesProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/context/ServicesProvider.tsx b/frontend/context/ServicesProvider.tsx index 8ccdb3e4..0f2ea99b 100644 --- a/frontend/context/ServicesProvider.tsx +++ b/frontend/context/ServicesProvider.tsx @@ -75,7 +75,7 @@ export const ServicesProvider = ({ children }: PropsWithChildren) => { if (selectedServiceConfigId) return; // only select a service by default if services are fetched, but there has been no selection yet if (isFetched && services.length > 0 && !selectedServiceConfigId) - setSelectedServiceConfigId(services[0].service_config_id); // TODO: use uuid instead of hash once middleware refactored + setSelectedServiceConfigId(services[0].service_config_id); }, [isFetched, selectedServiceConfigId, services]); // const serviceAddresses = useMemo( From af620d18ffe48bfdd8d69128101411748859f9c1 Mon Sep 17 00:00:00 2001 From: truemiller Date: Wed, 13 Nov 2024 00:21:09 +0000 Subject: [PATCH 24/28] chore: rename balance of fragment --- frontend/abis/erc20.ts | 2 +- frontend/components/MainPage/sections/AddFundsSection.tsx | 6 +++--- frontend/service/Multicall.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/abis/erc20.ts b/frontend/abis/erc20.ts index aafcd708..2c32e58c 100644 --- a/frontend/abis/erc20.ts +++ b/frontend/abis/erc20.ts @@ -1,5 +1,5 @@ import { Abi } from '@/types/Abi'; -export const ERC20_BALANCEOF_STRING_FRAGMENT: Abi = [ +export const ERC20_BALANCE_OF_STRING_FRAGMENT: Abi = [ 'function balanceOf(address owner) view returns (uint256)', ]; diff --git a/frontend/components/MainPage/sections/AddFundsSection.tsx b/frontend/components/MainPage/sections/AddFundsSection.tsx index bd37ea48..44454622 100644 --- a/frontend/components/MainPage/sections/AddFundsSection.tsx +++ b/frontend/components/MainPage/sections/AddFundsSection.tsx @@ -17,7 +17,7 @@ import { forwardRef, useCallback, useMemo, useRef, useState } from 'react'; import styled from 'styled-components'; import { useInterval } from 'usehooks-ts'; -import { ERC20_BALANCEOF_STRING_FRAGMENT } from '@/abis/erc20'; +import { ERC20_BALANCE_OF_STRING_FRAGMENT } from '@/abis/erc20'; import { CHAIN_CONFIG } from '@/config/chains'; import { TOKEN_CONFIG } from '@/config/tokens'; import { UNICODE_SYMBOLS } from '@/constants/symbols'; @@ -111,7 +111,7 @@ export const OpenAddFundsSection = forwardRef((_, ref) => { //USDC balance new Contract( TOKEN_CONFIG[CHAIN_CONFIG.ETHEREUM.chainId]['USDC'].address, - ERC20_BALANCEOF_STRING_FRAGMENT, + ERC20_BALANCE_OF_STRING_FRAGMENT, ETHEREUM_PROVIDER, ) .balanceOf(masterSafeAddress) @@ -124,7 +124,7 @@ export const OpenAddFundsSection = forwardRef((_, ref) => { .then(setopEth), new Contract( TOKEN_CONFIG[CHAIN_CONFIG.OPTIMISM.chainId]['OLAS'].address, - ERC20_BALANCEOF_STRING_FRAGMENT, + ERC20_BALANCE_OF_STRING_FRAGMENT, OPTIMISM_PROVIDER, ) .balanceOf(masterSafeAddress) diff --git a/frontend/service/Multicall.ts b/frontend/service/Multicall.ts index 6ba2f64c..668247f9 100644 --- a/frontend/service/Multicall.ts +++ b/frontend/service/Multicall.ts @@ -1,7 +1,7 @@ import { ethers } from 'ethers'; import { Contract as MulticallContract, ContractCall } from 'ethers-multicall'; -import { ERC20_BALANCEOF_STRING_FRAGMENT } from '@/abis/erc20'; +import { ERC20_BALANCE_OF_STRING_FRAGMENT } from '@/abis/erc20'; import { Erc20TokenConfig } from '@/config/tokens'; import { PROVIDERS } from '@/constants/providers'; import { ChainId } from '@/enums/Chain'; @@ -59,7 +59,7 @@ const getErc20Balances = async ( const callData: ContractCall[] = addresses.map((address: Address) => new MulticallContract( erc20TokenConfig.address, - ERC20_BALANCEOF_STRING_FRAGMENT, + ERC20_BALANCE_OF_STRING_FRAGMENT, ) .balanceOf(address) .then((balance: bigint) => From 6240763eb6973242fed7becdcaf34add44b87cb0 Mon Sep 17 00:00:00 2001 From: truemiller Date: Wed, 13 Nov 2024 00:22:35 +0000 Subject: [PATCH 25/28] chore: extend commented hide top bar --- frontend/components/MainPage/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/components/MainPage/index.tsx b/frontend/components/MainPage/index.tsx index dc196cfa..c425294b 100644 --- a/frontend/components/MainPage/index.tsx +++ b/frontend/components/MainPage/index.tsx @@ -36,7 +36,7 @@ export const Main = () => { // const hideMainOlasBalanceTopBorder = [ // !backupSafeAddress, - // currentStakingProgram === StakingProgramId.Alpha, + // currentStakingProgram === StakingProgramId.PearlAlpha, // ].some((condition) => !!condition); return ( From 3a6c155369defb2fa2983d9cadc9df568642c86d Mon Sep 17 00:00:00 2001 From: Josh Miller <31908788+truemiller@users.noreply.github.com> Date: Wed, 13 Nov 2024 10:14:53 +0000 Subject: [PATCH 26/28] Update frontend/components/MainPage/header/AgentButton/AgentButton.tsx Co-authored-by: Mohan --- frontend/components/MainPage/header/AgentButton/AgentButton.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/components/MainPage/header/AgentButton/AgentButton.tsx b/frontend/components/MainPage/header/AgentButton/AgentButton.tsx index 11b3a4cf..d5cf98f2 100644 --- a/frontend/components/MainPage/header/AgentButton/AgentButton.tsx +++ b/frontend/components/MainPage/header/AgentButton/AgentButton.tsx @@ -17,13 +17,11 @@ import { AgentStoppingButton } from './AgentStoppingButton'; export const AgentButton = () => { const { selectedService } = useServices(); - const { service, deploymentStatus: serviceStatus, isLoaded, } = useService({ serviceConfigId: selectedService?.service_config_id }); - const { isEligibleForStaking, isAgentEvicted } = useStakingContractInfo(); return useMemo(() => { From d52558d9c3b5c192c656c6ec288284ed40e3b350 Mon Sep 17 00:00:00 2001 From: Josh Miller <31908788+truemiller@users.noreply.github.com> Date: Wed, 13 Nov 2024 10:33:13 +0000 Subject: [PATCH 27/28] Update frontend/utils/numberFormatters.ts Co-authored-by: Mohan --- frontend/utils/numberFormatters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/utils/numberFormatters.ts b/frontend/utils/numberFormatters.ts index ceb28dd9..88411c85 100644 --- a/frontend/utils/numberFormatters.ts +++ b/frontend/utils/numberFormatters.ts @@ -32,7 +32,7 @@ export const formatEther = (wei: BigNumberish): string => { }; /** - * Parse converts small numbers into larger numbers + * Converts small numbers into larger numbers * @note **multiplies** the input by `10 ** decimals` * @example parseUnits('1.0', 18) => '1000000000000000000' */ From 2b2ba5a66d479181e2d3d1fb11ee14ee2156e074 Mon Sep 17 00:00:00 2001 From: truemiller Date: Wed, 13 Nov 2024 14:15:47 +0000 Subject: [PATCH 28/28] refactor: remove unused constants from MainPage header --- frontend/components/MainPage/header/constants.ts | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 frontend/components/MainPage/header/constants.ts diff --git a/frontend/components/MainPage/header/constants.ts b/frontend/components/MainPage/header/constants.ts deleted file mode 100644 index 27f36a98..00000000 --- a/frontend/components/MainPage/header/constants.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { formatUnits } from 'ethers/lib/utils'; - -import { CHAIN_CONFIG } from '@/config/chains'; -import { SERVICE_TEMPLATES } from '@/constants/serviceTemplates'; - -export const requiredGas = Number( - formatUnits( - `${SERVICE_TEMPLATES[0].configurations[CHAIN_CONFIG.OPTIMISM.chainId].monthly_gas_estimate}`, - 18, - ), -);