diff --git a/frontend/components/Main/MainGasBalance.tsx b/frontend/components/Main/MainGasBalance.tsx index 0d1f83f26..f3b14438d 100644 --- a/frontend/components/Main/MainGasBalance.tsx +++ b/frontend/components/Main/MainGasBalance.tsx @@ -4,7 +4,6 @@ import { useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; import { COLOR } from '@/constants/colors'; -import { LOW_BALANCE } from '@/constants/thresholds'; import { useBalance } from '@/hooks/useBalance'; import { useElectronApi } from '@/hooks/useElectronApi'; import { useStore } from '@/hooks/useStore'; @@ -34,7 +33,7 @@ const FineDot = styled(Dot)` `; const BalanceStatus = () => { - const { isBalanceLoaded, safeBalance } = useBalance(); + const { isBalanceLoaded, isLowBalance } = useBalance(); const { storeState } = useStore(); const { showNotification } = useElectronApi(); @@ -44,35 +43,34 @@ const BalanceStatus = () => { // show notification if balance is too low useEffect(() => { if (!isBalanceLoaded) return; - if (!safeBalance) return; if (!showNotification) return; if (!storeState?.isInitialFunded) return; - if (safeBalance.ETH < LOW_BALANCE && !isLowBalanceNotificationShown) { + if (isLowBalance && !isLowBalanceNotificationShown) { showNotification('Trading balance is too low.'); setIsLowBalanceNotificationShown(true); } // If it has already been shown and the balance has increased, // should show the notification again if it goes below the threshold. - if (safeBalance.ETH >= LOW_BALANCE && isLowBalanceNotificationShown) { + if (!isLowBalance && isLowBalanceNotificationShown) { setIsLowBalanceNotificationShown(false); } }, [ isBalanceLoaded, isLowBalanceNotificationShown, - safeBalance, + isLowBalance, showNotification, storeState?.isInitialFunded, ]); const status = useMemo(() => { - if (!safeBalance || safeBalance.ETH < LOW_BALANCE) { + if (isLowBalance) { return { statusName: 'Too low', StatusComponent: EmptyDot }; } return { statusName: 'Fine', StatusComponent: FineDot }; - }, [safeBalance]); + }, [isLowBalance]); const { statusName, StatusComponent } = status; return ( diff --git a/frontend/components/Main/MainHeader/AgentButton/index.tsx b/frontend/components/Main/MainHeader/AgentButton/index.tsx index 356a45c12..42c060200 100644 --- a/frontend/components/Main/MainHeader/AgentButton/index.tsx +++ b/frontend/components/Main/MainHeader/AgentButton/index.tsx @@ -4,7 +4,6 @@ import { useCallback, useMemo } from 'react'; import { Chain, DeploymentStatus } from '@/client'; import { COLOR } from '@/constants/colors'; -import { LOW_BALANCE } from '@/constants/thresholds'; import { useBalance } from '@/hooks/useBalance'; import { useElectronApi } from '@/hooks/useElectronApi'; import { useServices } from '@/hooks/useServices'; @@ -101,6 +100,7 @@ const AgentNotRunningButton = () => { const { setIsPaused: setIsBalancePollingPaused, safeBalance, + isLowBalance, totalOlasStakedBalance, totalEthBalance, } = useBalance(); @@ -194,7 +194,7 @@ const AgentNotRunningButton = () => { const isServiceInactive = serviceStatus === DeploymentStatus.BUILT || serviceStatus === DeploymentStatus.STOPPED; - if (isServiceInactive && safeBalance && safeBalance.ETH < LOW_BALANCE) { + if (isServiceInactive && isLowBalance) { return false; } @@ -224,7 +224,7 @@ const AgentNotRunningButton = () => { serviceStatus, storeState?.isInitialFunded, totalEthBalance, - safeBalance, + isLowBalance, ]); const buttonProps: ButtonProps = { diff --git a/frontend/components/Main/MainHeader/index.tsx b/frontend/components/Main/MainHeader/index.tsx index 78ffde06f..7dbd5560d 100644 --- a/frontend/components/Main/MainHeader/index.tsx +++ b/frontend/components/Main/MainHeader/index.tsx @@ -2,7 +2,6 @@ import { Flex } from 'antd'; import { useCallback, useEffect, useState } from 'react'; import { DeploymentStatus } from '@/client'; -import { LOW_BALANCE } from '@/constants/thresholds'; import { useBalance } from '@/hooks/useBalance'; import { useElectronApi } from '@/hooks/useElectronApi'; import { useServices } from '@/hooks/useServices'; @@ -12,12 +11,12 @@ import { AgentHead } from './AgentHead'; import { FirstRunModal } from './FirstRunModal'; const useSetupTrayIcon = () => { - const { safeBalance } = useBalance(); + const { isLowBalance } = useBalance(); const { serviceStatus } = useServices(); const { setTrayIcon } = useElectronApi(); useEffect(() => { - if (safeBalance && safeBalance.ETH < LOW_BALANCE) { + if (isLowBalance) { setTrayIcon?.('low-gas'); } else if (serviceStatus === DeploymentStatus.DEPLOYED) { setTrayIcon?.('running'); @@ -26,7 +25,7 @@ const useSetupTrayIcon = () => { } else if (serviceStatus === DeploymentStatus.BUILT) { setTrayIcon?.('logged-out'); } - }, [safeBalance, serviceStatus, setTrayIcon]); + }, [isLowBalance, serviceStatus, setTrayIcon]); return null; }; diff --git a/frontend/components/Main/MainOlasBalance.tsx b/frontend/components/Main/MainOlasBalance.tsx index fa6d87b9b..9b11ebec7 100644 --- a/frontend/components/Main/MainOlasBalance.tsx +++ b/frontend/components/Main/MainOlasBalance.tsx @@ -122,13 +122,12 @@ const MainOlasBalanceAlert = styled.div` `; const LowTradingBalanceAlert = () => { - const { isBalanceLoaded, safeBalance } = useBalance(); + const { isBalanceLoaded, isLowBalance } = useBalance(); const { storeState } = useStore(); if (!isBalanceLoaded) return null; - if (!safeBalance) return null; if (!storeState?.isInitialFunded) return; - if (safeBalance.ETH >= LOW_BALANCE) return null; + if (!isLowBalance) return null; return ( diff --git a/frontend/constants/thresholds.ts b/frontend/constants/thresholds.ts index fb5ab3515..454c1d224 100644 --- a/frontend/constants/thresholds.ts +++ b/frontend/constants/thresholds.ts @@ -1,4 +1,4 @@ -import { Chain } from '@/client'; +import { Chain } from "@/client"; export const MIN_ETH_BALANCE_THRESHOLDS = { [Chain.GNOSIS]: { @@ -7,4 +7,5 @@ export const MIN_ETH_BALANCE_THRESHOLDS = { }, }; +export const LOW_AGENT_BALANCE = 0.5; export const LOW_BALANCE = 2; diff --git a/frontend/context/BalanceProvider.tsx b/frontend/context/BalanceProvider.tsx index 12c5400ec..60e771919 100644 --- a/frontend/context/BalanceProvider.tsx +++ b/frontend/context/BalanceProvider.tsx @@ -1,7 +1,7 @@ -import { message } from 'antd'; -import { isAddress } from 'ethers/lib/utils'; -import { isNumber } from 'lodash'; -import { ValueOf } from 'next/dist/shared/lib/constants'; +import { message } from "antd"; +import { isAddress } from "ethers/lib/utils"; +import { isNumber } from "lodash"; +import { ValueOf } from "next/dist/shared/lib/constants"; import { createContext, Dispatch, @@ -11,27 +11,28 @@ import { useContext, useMemo, useState, -} from 'react'; -import { useInterval } from 'usehooks-ts'; - -import { Wallet } from '@/client'; -import { FIVE_SECONDS_INTERVAL } from '@/constants/intervals'; -import { TOKENS } from '@/constants/tokens'; -import { ServiceRegistryL2ServiceState } from '@/enums/ServiceRegistryL2ServiceState'; -import { Token } from '@/enums/Token'; -import { AutonolasService } from '@/service/Autonolas'; -import { EthersService } from '@/service/Ethers'; -import MulticallService from '@/service/Multicall'; -import { Address } from '@/types/Address'; +} from "react"; +import { useInterval } from "usehooks-ts"; + +import { Wallet } from "@/client"; +import { FIVE_SECONDS_INTERVAL } from "@/constants/intervals"; +import { TOKENS } from "@/constants/tokens"; +import { ServiceRegistryL2ServiceState } from "@/enums/ServiceRegistryL2ServiceState"; +import { Token } from "@/enums/Token"; +import { AutonolasService } from "@/service/Autonolas"; +import { EthersService } from "@/service/Ethers"; +import MulticallService from "@/service/Multicall"; +import { Address } from "@/types/Address"; import { AddressNumberRecord, WalletAddressNumberRecord, -} from '@/types/Records'; +} from "@/types/Records"; -import { OnlineStatusContext } from './OnlineStatusProvider'; -import { RewardContext } from './RewardProvider'; -import { ServicesContext } from './ServicesProvider'; -import { WalletContext } from './WalletProvider'; +import { OnlineStatusContext } from "./OnlineStatusProvider"; +import { RewardContext } from "./RewardProvider"; +import { ServicesContext } from "./ServicesProvider"; +import { WalletContext } from "./WalletProvider"; +import { LOW_AGENT_BALANCE, LOW_BALANCE } from "@/constants/thresholds"; export const BalanceContext = createContext<{ isLoaded: boolean; @@ -43,6 +44,7 @@ export const BalanceContext = createContext<{ safeBalance?: ValueOf; totalEthBalance?: number; totalOlasBalance?: number; + isLowBalance: boolean; wallets?: Wallet[]; walletBalances: WalletAddressNumberRecord; updateBalances: () => Promise; @@ -58,6 +60,7 @@ export const BalanceContext = createContext<{ safeBalance: undefined, totalEthBalance: undefined, totalOlasBalance: undefined, + isLowBalance: false, wallets: undefined, walletBalances: {}, updateBalances: async () => {}, @@ -85,7 +88,7 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { if (!isLoaded) return; return Object.values(walletBalances).reduce( (acc: number, walletBalance) => acc + walletBalance.ETH, - 0, + 0 ); }, [isLoaded, walletBalances]); @@ -94,7 +97,7 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { const sumWalletBalances = Object.values(walletBalances).reduce( (acc: number, walletBalance) => acc + walletBalance.OLAS, - 0, + 0 ); const total = @@ -146,7 +149,7 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { const { depositValue, bondValue, serviceState } = await AutonolasService.getServiceRegistryInfo( masterSafeAddress, - serviceId, + serviceId ); switch (serviceState) { @@ -182,25 +185,41 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { setIsBalanceLoaded(true); } catch (error) { console.error(error); - message.error('Unable to retrieve wallet balances'); + message.error("Unable to retrieve wallet balances"); setIsBalanceLoaded(true); } }, [masterEoaAddress, masterSafeAddress, serviceAddresses, services]); const eoaBalance = useMemo( () => masterEoaAddress && walletBalances[masterEoaAddress], - [masterEoaAddress, walletBalances], + [masterEoaAddress, walletBalances] ); const safeBalance = useMemo( () => masterSafeAddress && walletBalances[masterSafeAddress], - [masterSafeAddress, walletBalances], + [masterSafeAddress, walletBalances] ); + const agentSafeBalance = useMemo( + () => + services?.[0]?.chain_data?.multisig && + walletBalances[services[0].chain_data.multisig], + [services, walletBalances] + ); + const isLowBalance = useMemo(() => { + if (!safeBalance || !agentSafeBalance) return false; + if ( + safeBalance.ETH < LOW_BALANCE && + // Need to check agentSafe balance as well, because it's auto-funded from safeBalance + agentSafeBalance.ETH < LOW_AGENT_BALANCE + ) + return true; + return false; + }, [safeBalance, agentSafeBalance]); useInterval( () => { updateBalances(); }, - isPaused || !isOnline ? null : FIVE_SECONDS_INTERVAL, + isPaused || !isOnline ? null : FIVE_SECONDS_INTERVAL ); return ( @@ -215,6 +234,7 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { safeBalance, totalEthBalance, totalOlasBalance, + isLowBalance, wallets, walletBalances, updateBalances, @@ -228,7 +248,7 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { }; export const getEthBalances = async ( - walletAddresses: Address[], + walletAddresses: Address[] ): Promise => { const rpcIsValid = await EthersService.checkRpc(`${process.env.GNOSIS_RPC}`); if (!rpcIsValid) return; @@ -239,14 +259,14 @@ export const getEthBalances = async ( }; export const getOlasBalances = async ( - walletAddresses: Address[], + walletAddresses: Address[] ): Promise => { const rpcIsValid = await EthersService.checkRpc(`${process.env.GNOSIS_RPC}`); if (!rpcIsValid) return; const olasBalances = await MulticallService.getErc20Balances( walletAddresses, - TOKENS.gnosis.OLAS, + TOKENS.gnosis.OLAS ); return olasBalances; @@ -254,7 +274,7 @@ export const getOlasBalances = async ( export const getWalletAddresses = ( wallets: Wallet[], - serviceAddresses: Address[], + serviceAddresses: Address[] ): Address[] => { const walletsToCheck: Address[] = []; @@ -278,7 +298,7 @@ export const getWalletAddresses = ( }; export const getWalletBalances = async ( - walletAddresses: Address[], + walletAddresses: Address[] ): Promise => { const [ethBalances, olasBalances] = await Promise.all([ getEthBalances(walletAddresses), diff --git a/frontend/context/WalletProvider.tsx b/frontend/context/WalletProvider.tsx index 21c02606a..368274970 100644 --- a/frontend/context/WalletProvider.tsx +++ b/frontend/context/WalletProvider.tsx @@ -1,12 +1,12 @@ -import { createContext, PropsWithChildren, useContext, useState } from 'react'; -import { useInterval } from 'usehooks-ts'; +import { createContext, PropsWithChildren, useContext, useState } from "react"; +import { useInterval } from "usehooks-ts"; -import { Wallet } from '@/client'; -import { FIVE_SECONDS_INTERVAL } from '@/constants/intervals'; -import { WalletService } from '@/service/Wallet'; -import { Address } from '@/types/Address'; +import { Wallet } from "@/client"; +import { FIVE_SECONDS_INTERVAL } from "@/constants/intervals"; +import { WalletService } from "@/service/Wallet"; +import { Address } from "@/types/Address"; -import { OnlineStatusContext } from './OnlineStatusProvider'; +import { OnlineStatusContext } from "./OnlineStatusProvider"; export const WalletContext = createContext<{ masterEoaAddress?: Address;