Skip to content

Commit

Permalink
Merge branch 'refactor/multi-chain-support-frontend' of github.com-pe…
Browse files Browse the repository at this point in the history
…rsonal:valory-xyz/olas-operate-app into mohan/transaction-history
  • Loading branch information
mohandast52 committed Nov 21, 2024
2 parents 920a53f + 4c80e72 commit e286eba
Show file tree
Hide file tree
Showing 15 changed files with 310 additions and 146 deletions.
2 changes: 2 additions & 0 deletions frontend/client/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { AgentType } from '@/enums/Agent';
import { StakingProgramId } from '@/enums/StakingProgram';
import { Address } from '@/types/Address';

Expand Down Expand Up @@ -70,6 +71,7 @@ export type EnvVariableAttributes = {
};

export type ServiceTemplate = {
agentType: AgentType;
name: string;
hash: string;
description: string;
Expand Down
12 changes: 7 additions & 5 deletions frontend/components/MainPage/header/AgentButton/AgentButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useMemo } from 'react';
import { MiddlewareDeploymentStatus } from '@/client';
import { useService } from '@/hooks/useService';
import { useServices } from '@/hooks/useServices';
import { useStakingContractDetails } from '@/hooks/useStakingContractDetails';
import { useActiveStakingContractInfo } from '@/hooks/useStakingContractDetails';
import { useStakingProgram } from '@/hooks/useStakingProgram';
import { assertRequired } from '@/types/Util';

Expand All @@ -21,20 +21,22 @@ export const AgentButton = () => {
const { selectedService } = useServices();
const { activeStakingProgramId } = useStakingProgram();

const serviceConfigId = selectedService?.service_config_id;

const {
service,
deploymentStatus: serviceStatus,
isLoaded,
} = useService({ serviceConfigId: selectedService?.service_config_id });
} = useService({ serviceConfigId });

assertRequired(
// TODO: review whether this causes agent button to not render
activeStakingProgramId,
'Active staking program ID is required',
);

const { isEligibleForStaking, isAgentEvicted } = useStakingContractDetails(
activeStakingProgramId,
);
const { isEligibleForStaking, isAgentEvicted } =
useActiveStakingContractInfo();

return useMemo(() => {
if (!isLoaded) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { Button, ButtonProps } from 'antd';
import { useCallback, useMemo } from 'react';

import { MiddlewareChain, MiddlewareDeploymentStatus } from '@/client';
import { MechType } from '@/config/mechs';
import { STAKING_PROGRAMS } from '@/config/stakingPrograms';
import { SERVICE_TEMPLATES } from '@/constants/serviceTemplates';
import { LOW_MASTER_SAFE_BALANCE } from '@/constants/thresholds';
import { TokenSymbol } from '@/enums/Token';
import {
Expand All @@ -12,11 +14,9 @@ import {
import { useElectronApi } from '@/hooks/useElectronApi';
import { useService } from '@/hooks/useService';
import { useServices } from '@/hooks/useServices';
import { useServiceTemplates } from '@/hooks/useServiceTemplates';
import {
useActiveStakingContractInfo,
useStakingContractContext,
useStakingContractDetails,
} from '@/hooks/useStakingContractDetails';
import { useStakingProgram } from '@/hooks/useStakingProgram';
import { useStore } from '@/hooks/useStore';
Expand All @@ -26,7 +26,7 @@ import { WalletService } from '@/service/Wallet';
import { delayInSeconds } from '@/utils/delay';

/** Button used to start / deploy the agent */
export const AgentNotRunningButton = (serviceConfigId: string) => {
export const AgentNotRunningButton = () => {
const { masterWallets: wallets } = useMasterWalletContext();
const {
selectedService,
Expand All @@ -38,16 +38,16 @@ export const AgentNotRunningButton = (serviceConfigId: string) => {
serviceConfigId:
isLoaded && selectedService ? selectedService?.service_config_id : '',
});
const { serviceTemplate } = useServiceTemplates();
const { showNotification } = useElectronApi();
const {
setIsPaused: setIsBalancePollingPaused,
totalStakedOlasBalance,
totalEthBalance,
updateBalances,
} = useBalanceContext();
const { serviceSafeBalances, isLowBalance } =
useServiceBalances(serviceConfigId);
const { serviceSafeBalances, isLowBalance } = useServiceBalances(
selectedService?.service_config_id,
);
const { storeState } = useStore();
const {
isAllStakingContractDetailsRecordLoaded,
Expand All @@ -57,12 +57,11 @@ export const AgentNotRunningButton = (serviceConfigId: string) => {
const { activeStakingProgramId } = useStakingProgram();
const { isEligibleForStaking, isAgentEvicted, isServiceStaked } =
useActiveStakingContractInfo();
const { hasEnoughServiceSlots } = useStakingContractDetails(
activeStakingProgramId,
);
const { hasEnoughServiceSlots } = useActiveStakingContractInfo();

const requiredStakedOlas =
service &&
activeStakingProgramId &&
STAKING_PROGRAMS[service.home_chain_id][activeStakingProgramId]
?.stakingRequirements[TokenSymbol.OLAS];

Expand Down Expand Up @@ -102,15 +101,22 @@ export const AgentNotRunningButton = (serviceConfigId: string) => {
}, [service]);

const deployAndStartService = useCallback(async () => {
await ServicesService.createService({
if (!activeStakingProgramId) return;

const middlewareServiceResponse = await ServicesService.createService({
stakingProgramId: activeStakingProgramId,
serviceTemplate,
serviceTemplate: SERVICE_TEMPLATES[0], // TODO: support multi-agent, during optimus week
deploy: true,
useMechMarketplace: false,
useMechMarketplace:
STAKING_PROGRAMS[+SERVICE_TEMPLATES[0].home_chain_id][ // TODO: support multi-agent, during optimus week
activeStakingProgramId
].mechType === MechType.Marketplace,
});

await ServicesService.startService(serviceConfigId);
}, [activeStakingProgramId, serviceTemplate, serviceConfigId]);
await ServicesService.startService(
middlewareServiceResponse.service_config_id,
);
}, [activeStakingProgramId]);

const updateStatesSequentially = useCallback(async () => {
await updateServicesState?.();
Expand Down
5 changes: 2 additions & 3 deletions frontend/components/MainPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { usePageState } from '@/hooks/usePageState';
import { MainHeader } from './header';
import { AddFundsSection } from './sections/AddFundsSection';
import { AlertSections } from './sections/AlertSections';
import { GasBalanceSection } from './sections/GasBalanceSection';
import { KeepAgentRunningSection } from './sections/KeepAgentRunningSection';
import { MainNeedsFunds } from './sections/NeedsFundsSection';
import { MainOlasBalance } from './sections/OlasBalanceSection';
Expand Down Expand Up @@ -87,11 +86,11 @@ export const Main = () => {
<Flex vertical>
<AlertSections />
<MainOlasBalance isBorderTopVisible={false} />
<MainOlasBalance isBorderTopVisible={!hideMainOlasBalanceTopBorder} />
{/* <MainOlasBalance isBorderTopVisible={!hideMainOlasBalanceTopBorder} /> */}
<RewardsSection />
<KeepAgentRunningSection />
<StakingContractUpdate />
<GasBalanceSection />
{/* <GasBalanceSection /> */}
<MainNeedsFunds />
<AddFundsSection />
</Flex>
Expand Down
11 changes: 8 additions & 3 deletions frontend/components/MainPage/sections/NeedsFundsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { CustomAlert } from '@/components/Alert';
import { UNICODE_SYMBOLS } from '@/constants/symbols';
import { useElectronApi } from '@/hooks/useElectronApi';
import { useNeedsFunds } from '@/hooks/useNeedsFunds';
import { useServices } from '@/hooks/useServices';

import { CardSection } from '../../styled/CardSection';

Expand All @@ -28,6 +29,9 @@ export const MainNeedsFunds = () => {
needsInitialFunding,
} = useNeedsFunds();

const { selectedAgentConfig } = useServices();
const { homeChainId } = selectedAgentConfig;

const electronApi = useElectronApi();

const message: ReactNode = useMemo(
Expand All @@ -37,14 +41,14 @@ export const MainNeedsFunds = () => {
<Flex gap={24}>
{!hasEnoughOlasForInitialFunding && (
<div>
<FundingValue>{`${UNICODE_SYMBOLS.OLAS}${serviceFundRequirements.olas} OLAS `}</FundingValue>
<FundingValue>{`${UNICODE_SYMBOLS.OLAS}${serviceFundRequirements[homeChainId].olas} OLAS `}</FundingValue>
<span className="text-sm">for staking</span>
</div>
)}
{!hasEnoughEthForInitialFunding && (
<div>
<FundingValue>
{`$${serviceFundRequirements.eth} XDAI `}
{`$${serviceFundRequirements[homeChainId].eth} XDAI `}
</FundingValue>
<span className="text-sm">for trading</span>
</div>
Expand All @@ -56,9 +60,10 @@ export const MainNeedsFunds = () => {
</Flex>
),
[
hasEnoughOlasForInitialFunding,
serviceFundRequirements,
homeChainId,
hasEnoughEthForInitialFunding,
hasEnoughOlasForInitialFunding,
],
);

Expand Down
57 changes: 51 additions & 6 deletions frontend/components/MainPage/sections/OlasBalanceSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@ import { useMemo } from 'react';
import styled from 'styled-components';

import { UNICODE_SYMBOLS } from '@/constants/symbols';
import { TokenSymbol } from '@/enums/Token';
// import { Pages } from '@/enums/PageState';
import { useBalanceContext } from '@/hooks/useBalanceContext';
import {
useBalanceContext,
useMasterBalances,
useServiceBalances,
} from '@/hooks/useBalanceContext';
import { useServices } from '@/hooks/useServices';
// import { usePageState } from '@/hooks/usePageState';
import { balanceFormat } from '@/utils/numberFormatters';

Expand All @@ -22,13 +28,52 @@ type MainOlasBalanceProps = { isBorderTopVisible?: boolean };
export const MainOlasBalance = ({
isBorderTopVisible = true,
}: MainOlasBalanceProps) => {
const { isLoaded: isBalanceLoaded, totalOlasBalance } = useBalanceContext();
const { selectedService } = useServices();
const { isLoaded: isBalanceLoaded } = useBalanceContext();
const { masterWalletBalances } = useMasterBalances();
const { serviceStakedBalances, serviceWalletBalances } = useServiceBalances(
selectedService?.service_config_id,
);
// const { goto } = usePageState();

const balance = useMemo(() => {
if (totalOlasBalance === undefined) return '--';
const displayedBalance = useMemo(() => {
// olas across master wallets, safes and eoa
const masterWalletOlasBalance = masterWalletBalances?.reduce(
(acc, { symbol, balance }) => {
if (symbol === TokenSymbol.OLAS) {
return acc + Number(balance);
}
return acc;
},
0,
);

// olas across all service wallets
const serviceWalletOlasBalance = serviceWalletBalances?.reduce(
(acc, { symbol, balance }) => {
if (symbol === TokenSymbol.OLAS) {
return acc + Number(balance);
}
return acc;
},
0,
);

// olas staked across all services
const serviceStakedOlasBalance = serviceStakedBalances?.reduce(
(acc, { olasBondBalance, olasDepositBalance }) => {
return acc + Number(olasBondBalance) + Number(olasDepositBalance);
},
0,
);

const totalOlasBalance =
masterWalletOlasBalance +
serviceWalletOlasBalance +
serviceStakedOlasBalance;

return balanceFormat(totalOlasBalance, 2);
}, [totalOlasBalance]);
}, [masterWalletBalances, serviceStakedBalances, serviceWalletBalances]);

return (
<CardSection
Expand All @@ -43,7 +88,7 @@ export const MainOlasBalance = ({
<Text type="secondary">Current balance</Text>
<Flex align="end">
<span className="balance-symbol">{UNICODE_SYMBOLS.OLAS}</span>
<Balance className="balance">{balance}</Balance>
<Balance className="balance">{displayedBalance}</Balance>
<span className="balance-currency">OLAS</span>
</Flex>

Expand Down
42 changes: 32 additions & 10 deletions frontend/components/SettingsPage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CloseOutlined, SettingOutlined } from '@ant-design/icons';
import { Button, Card, Flex, Typography } from 'antd';
import { isNil } from 'lodash';
import Link from 'next/link';
import { useMemo } from 'react';

Expand All @@ -11,7 +12,9 @@ import { SettingsScreen } from '@/enums/SettingsScreen';
import { useMultisig } from '@/hooks/useMultisig';
import { usePageState } from '@/hooks/usePageState';
import { useSettings } from '@/hooks/useSettings';
import { useWalletContext } from '@/hooks/useWallet';
import { useMasterWalletContext } from '@/hooks/useWallet';
import { Address } from '@/types/Address';
import { Optional } from '@/types/Util';
import { truncateAddress } from '@/utils/truncate';

import { CustomAlert } from '../Alert';
Expand Down Expand Up @@ -83,15 +86,34 @@ export const Settings = () => {
};

const SettingsMain = () => {
const { wallets } = useWalletContext();
const { backupSafeAddress } = useMultisig();
const { masterEoa, masterSafes } = useMasterWalletContext();

const { owners } = useMultisig(
masterSafes?.[0], // TODO: all master safes should have the same address, but dirty implementation
);

const { goto } = usePageState();

const truncatedBackupSafeAddress: string | undefined = useMemo(() => {
if (backupSafeAddress) {
return truncateAddress(backupSafeAddress);
const masterSafeBackupAddresses = useMemo<Optional<Address[]>>(() => {
if (!owners) return;
if (!masterEoa) return;

// TODO: handle edge cases where there are multiple owners due to middleware failure, or user interaction via safe.global
return owners.filter((owner) => owner !== masterEoa.address);
}, [owners, masterEoa]);

const masterSafeBackupAddress = useMemo<Optional<Address>>(() => {
if (isNil(masterSafeBackupAddresses)) return;
if (!masterSafeBackupAddresses?.[0]) return;

return masterSafeBackupAddresses[0];
}, [masterSafeBackupAddresses]);

const truncatedBackupSafeAddress: Optional<string> = useMemo(() => {
if (masterSafeBackupAddress && masterSafeBackupAddress?.length) {
return truncateAddress(masterSafeBackupAddress);
}
}, [backupSafeAddress]);
}, [masterSafeBackupAddress]);

return (
<Card
Expand Down Expand Up @@ -127,16 +149,16 @@ const SettingsMain = () => {
{/* Wallet backup */}
<CardSection
padding="24px"
borderbottom={backupSafeAddress ? 'true' : 'false'}
borderbottom={masterSafeBackupAddress ? 'true' : 'false'}
vertical
gap={8}
>
<Text strong>Backup wallet</Text>
{backupSafeAddress ? (
{masterSafeBackupAddress ? (
<Link
type="link"
target="_blank"
href={`${EXPLORER_URL[MiddlewareChain.OPTIMISM]}/address/${backupSafeAddress}`}
href={`${EXPLORER_URL[MiddlewareChain.GNOSIS]}/address/${masterSafeBackupAddress}`} // TODO: dynamic by selected agent type's home_chain_id
>
{truncatedBackupSafeAddress} {UNICODE_SYMBOLS.EXTERNAL_LINK}
</Link>
Expand Down
Loading

0 comments on commit e286eba

Please sign in to comment.