diff --git a/frontend/helpers/copyToClipboard.ts b/frontend/common-util/copyToClipboard.ts
similarity index 100%
rename from frontend/helpers/copyToClipboard.ts
rename to frontend/common-util/copyToClipboard.ts
diff --git a/frontend/components/Modals/QRModal.tsx b/frontend/components/Modals/QRModal.tsx
index a605ec461..41191dd22 100644
--- a/frontend/components/Modals/QRModal.tsx
+++ b/frontend/components/Modals/QRModal.tsx
@@ -9,7 +9,7 @@ type QRModalProps = {
};
export const QRModal = ({
- data: { amount, chainId, address, open, isERC20 },
+ data: { amount, chainId, address, open, isErc20: isERC20 },
}: QRModalProps) => {
const { setQrModalData } = useModals();
diff --git a/frontend/components/Spawn/Funding/FundRequirement/FundRequirement.tsx b/frontend/components/Spawn/Funding/FundRequirement/FundRequirement.tsx
index 27804576e..989d58dc2 100644
--- a/frontend/components/Spawn/Funding/FundRequirement/FundRequirement.tsx
+++ b/frontend/components/Spawn/Funding/FundRequirement/FundRequirement.tsx
@@ -1,4 +1,4 @@
-import { copyToClipboard } from '@/helpers/copyToClipboard';
+import { copyToClipboard } from '@/common-util/copyToClipboard';
import { useModals, useServices } from '@/hooks';
import { Address } from '@/types';
import { FundsReceivedMap } from '@/types';
@@ -70,7 +70,12 @@ export const FundRequirement = ({
const handleQr = useCallback(
(): void =>
- qrModalOpen({ amount: requirement, chainId: 100, address, isERC20 }), // hardcoded chainId for now
+ qrModalOpen({
+ amount: requirement,
+ chainId: 100,
+ address,
+ isErc20: isERC20,
+ }), // hardcoded chainId for now
[address, isERC20, qrModalOpen, requirement],
);
diff --git a/frontend/components/Spawn/Funding/FundRequirement/FundRequirementERC20.tsx b/frontend/components/Spawn/Funding/FundRequirement/FundRequirementERC20.tsx
index 1a15c049f..df633b0dc 100644
--- a/frontend/components/Spawn/Funding/FundRequirement/FundRequirementERC20.tsx
+++ b/frontend/components/Spawn/Funding/FundRequirement/FundRequirementERC20.tsx
@@ -14,7 +14,7 @@ type FundRequirementERC20Props = {
};
export const FundRequirementERC20 = (props: FundRequirementERC20Props) => {
- const { getERC20Balance } = useEthers();
+ const { getErc20Balance: getERC20Balance } = useEthers();
return (
);
diff --git a/frontend/components/Spawn/Funding/FundRequirement/FundRequirementETH.tsx b/frontend/components/Spawn/Funding/FundRequirement/FundRequirementETH.tsx
index a50b5c174..d3a0e091f 100644
--- a/frontend/components/Spawn/Funding/FundRequirement/FundRequirementETH.tsx
+++ b/frontend/components/Spawn/Funding/FundRequirement/FundRequirementETH.tsx
@@ -13,7 +13,7 @@ type FundRequirementETHProps = {
};
export const FundRequirementETH = (props: FundRequirementETHProps) => {
- const { getETHBalance } = useEthers();
+ const { getEthBalance: getETHBalance } = useEthers();
return (
);
diff --git a/frontend/components/Spawn/SpawnRPC.tsx b/frontend/components/Spawn/SpawnRPC.tsx
index e86e95ca6..ca519ecaf 100644
--- a/frontend/components/Spawn/SpawnRPC.tsx
+++ b/frontend/components/Spawn/SpawnRPC.tsx
@@ -17,7 +17,7 @@ import {
useMemo,
useState,
} from 'react';
-import { useSpawn, useEthers } from '@/hooks';
+import { useSpawn, useEthers, useAppInfo } from '@/hooks';
import { SpawnScreenState } from '@/enums';
import { CheckSquareTwoTone, WarningFilled } from '@ant-design/icons';
import { InputStatus } from 'antd/es/_util/statusUtils';
@@ -37,7 +37,8 @@ type SpawnRPCProps = {
export const SpawnRPC = ({ rpc, setRpc, nextPage }: SpawnRPCProps) => {
const { setSpawnScreenState } = useSpawn();
- const { checkRPC } = useEthers();
+ const { checkRPC, getEthBalance } = useEthers();
+ const { userPublicKey } = useAppInfo();
const [isCheckingRpc, setIsCheckingRpc] = useState(false);
const [rpcState, setRpcState] = useState(RPCState.INVALID);
@@ -82,12 +83,44 @@ export const SpawnRPC = ({ rpc, setRpc, nextPage }: SpawnRPCProps) => {
);
const handleContinue = useCallback(async () => {
+ if (!userPublicKey) {
+ message.error(
+ 'Error retrieving user public key, please ensure your master wallet key is set correctly',
+ );
+ return;
+ }
+
if (!rpc || rpcState !== RPCState.VALID) {
message.error('Invalid RPC');
return;
}
+
+ const ethBalance: number | undefined = await getEthBalance(
+ userPublicKey,
+ rpc,
+ ).catch(() => undefined);
+
+ if (ethBalance === undefined) {
+ message.error('Failed to get master wallet balance');
+ return;
+ }
+
+ if (ethBalance < 1) {
+ message.error(
+ 'Insufficient master wallet balance, you need at least 1 XDAI.',
+ );
+ return;
+ }
+
setSpawnScreenState(nextPage);
- }, [nextPage, rpc, rpcState, setSpawnScreenState]);
+ }, [
+ getEthBalance,
+ nextPage,
+ rpc,
+ rpcState,
+ setSpawnScreenState,
+ userPublicKey,
+ ]);
const isContinueDisabled: boolean = useMemo(
() => !rpc || rpcState !== RPCState.VALID,
diff --git a/frontend/components/Spawn/SpawnStakingCheck.tsx b/frontend/components/Spawn/SpawnStakingCheck.tsx
index 5990cda6b..7c1a219f2 100644
--- a/frontend/components/Spawn/SpawnStakingCheck.tsx
+++ b/frontend/components/Spawn/SpawnStakingCheck.tsx
@@ -39,7 +39,7 @@ export const SpawnStakingCheck = ({
}: SpawnStakingCheckProps) => {
const { createService } = useServices();
const { userPublicKey } = useAppInfo();
- const { getERC20Balance } = useEthers();
+ const { getErc20Balance } = useEthers();
const [isCreating, setIsCreating] = useState(false);
const [buttonClicked, setButtonClicked] = useState();
@@ -112,7 +112,7 @@ export const SpawnStakingCheck = ({
if (!userPublicKey) {
return Promise.reject('No public key found');
}
- return getERC20Balance(userPublicKey, rpc, TOKENS.gnosis.OLAS)
+ return getErc20Balance(userPublicKey, rpc, TOKENS.gnosis.OLAS)
.then((olasBalance: number) => {
const { olas_required_to_stake, olas_cost_of_bond } =
serviceTemplate.configuration;
@@ -132,7 +132,7 @@ export const SpawnStakingCheck = ({
.catch((e) => {
return Promise.reject(e);
});
- }, [getERC20Balance, userPublicKey, rpc, serviceTemplate.configuration]);
+ }, [getErc20Balance, userPublicKey, rpc, serviceTemplate.configuration]);
const handleYes = async () => {
setButtonClicked(ButtonOptions.YES);
@@ -180,19 +180,13 @@ export const SpawnStakingCheck = ({
setButtonClicked(undefined);
};
- const stakingRequirement = useMemo(
- () =>
- ethers.utils.formatUnits(
- `${
- serviceTemplate.configuration.olas_cost_of_bond +
- serviceTemplate.configuration.olas_required_to_stake
- }`,
- ),
- [
- serviceTemplate.configuration.olas_cost_of_bond,
- serviceTemplate.configuration.olas_required_to_stake,
- ],
- );
+ const stakingRequirement = useMemo(() => {
+ const { olas_required_to_stake, olas_cost_of_bond } =
+ serviceTemplate.configuration;
+ return ethers.utils.formatUnits(
+ `${olas_required_to_stake + olas_cost_of_bond}`,
+ );
+ }, [serviceTemplate.configuration]);
return (
diff --git a/frontend/components/YourAgents/HasAgents.tsx b/frontend/components/YourAgents/HasAgents.tsx
index 3c351b04e..8face25e4 100644
--- a/frontend/components/YourAgents/HasAgents.tsx
+++ b/frontend/components/YourAgents/HasAgents.tsx
@@ -1,12 +1,13 @@
import { Service } from '@/client';
import { Flex } from 'antd';
import { ServiceCard } from './ServiceCard/ServiceCard';
+import { ReactElement } from 'react';
export const HasAgents = ({
services,
}: {
services: Service[];
-}): JSX.Element => {
+}): ReactElement => {
return (
{services.map((service: Service) => (
diff --git a/frontend/components/YourAgents/ServiceCard/ServiceCardTotalBalance.tsx b/frontend/components/YourAgents/ServiceCard/ServiceCardTotalBalance.tsx
index 2e13025b3..5d4cec4fc 100644
--- a/frontend/components/YourAgents/ServiceCard/ServiceCardTotalBalance.tsx
+++ b/frontend/components/YourAgents/ServiceCard/ServiceCardTotalBalance.tsx
@@ -8,7 +8,7 @@ import { useInterval } from 'usehooks-ts';
const BALANCE_POLLING_INTERVAL = 5000;
export const ServiceCardTotalBalance = ({ service }: { service: Service }) => {
- const { getETHBalances } = useMulticall();
+ const { getEthBalances } = useMulticall();
const [hasInitialLoaded, setHasInitialLoaded] = useState(false);
const [balances, setBalances] = useState({});
@@ -27,7 +27,7 @@ export const ServiceCardTotalBalance = ({ service }: { service: Service }) => {
service.chain_data?.multisig &&
service.ledger?.rpc
)
- getETHBalances(
+ getEthBalances(
[...service.chain_data.instances, service.chain_data.multisig],
service.ledger.rpc,
)
diff --git a/frontend/context/ModalsProvider/ModalsProvider.tsx b/frontend/context/ModalsProvider/ModalsProvider.tsx
index 5277a9b19..4662a379f 100644
--- a/frontend/context/ModalsProvider/ModalsProvider.tsx
+++ b/frontend/context/ModalsProvider/ModalsProvider.tsx
@@ -24,7 +24,7 @@ export const ModalsContext = createContext({
address: undefined,
amount: undefined,
chainId: undefined,
- isERC20: false,
+ isErc20: false,
},
setQrModalData: () => {},
});
@@ -35,7 +35,7 @@ export const ModalsProvider = ({ children }: PropsWithChildren) => {
address: undefined,
amount: undefined,
chainId: undefined,
- isERC20: false,
+ isErc20: false,
});
return (
>;
+ setActiveTab: Dispatch>;
};
export const TabsContext = createContext({
@@ -18,9 +18,9 @@ export const TabsContext = createContext({
});
export const TabsProvider = ({ children }: PropsWithChildren) => {
- const [activeTab, setActiveTab] = useState(Tab.YOUR_AGENTS);
+ const [activeTab, setActiveTab] = useState(Tab.YOUR_AGENTS);
return (
-
+
{children}
);
diff --git a/frontend/hooks/useEthers.tsx b/frontend/hooks/useEthers.tsx
index d8970f3ce..0e60e80ee 100644
--- a/frontend/hooks/useEthers.tsx
+++ b/frontend/hooks/useEthers.tsx
@@ -1,5 +1,5 @@
import { Address } from '@/types';
-import { BigNumber, ethers, providers, utils } from 'ethers';
+import { ethers, providers, utils } from 'ethers';
export const useEthers = () => {
/**
@@ -8,7 +8,7 @@ export const useEthers = () => {
* @param rpc string
* @returns Promise
*/
- const getETHBalance = async (
+ const getEthBalance = async (
address: Address,
rpc: string,
): Promise => {
@@ -17,9 +17,10 @@ export const useEthers = () => {
name: 'Gnosis',
chainId: 100, // we currently only support Gnosis Trader agent
});
- return provider
- .getBalance(address)
- .then((balance: BigNumber) => Number(utils.formatEther(balance)));
+ return provider.getBalance(address).then((balance) => {
+ const formattedBalance = utils.formatEther(balance);
+ return Number(formattedBalance);
+ });
} catch (e) {
return Promise.reject('Failed to get ETH balance');
}
@@ -32,7 +33,7 @@ export const useEthers = () => {
* @param contractAddress Address
* @returns Promise
*/
- const getERC20Balance = async (
+ const getErc20Balance = async (
address: Address,
rpc: string,
contractAddress?: Address,
@@ -87,5 +88,9 @@ export const useEthers = () => {
}
};
- return { getETHBalance, checkRPC, getERC20Balance };
+ return {
+ getEthBalance,
+ checkRPC,
+ getErc20Balance,
+ };
};
diff --git a/frontend/hooks/useModals.tsx b/frontend/hooks/useModals.tsx
index ff289df5b..cb5e94620 100644
--- a/frontend/hooks/useModals.tsx
+++ b/frontend/hooks/useModals.tsx
@@ -13,7 +13,7 @@ export const useModals = () => {
address: undefined,
amount: undefined,
chainId: undefined,
- isERC20: false,
+ isErc20: false,
});
const qrModalOpen = (data: Omit, 'open'>) =>
diff --git a/frontend/hooks/useMulticall.tsx b/frontend/hooks/useMulticall.tsx
index a1b0c58fa..707830387 100644
--- a/frontend/hooks/useMulticall.tsx
+++ b/frontend/hooks/useMulticall.tsx
@@ -11,7 +11,7 @@ export const useMulticall = () => {
* @param rpc
* @returns Promise<{ [address: string]: number }>
*/
- const getETHBalances = async (
+ const getEthBalances = async (
addresses: Address[],
rpc: string,
): Promise<{ [address: Address]: number }> => {
@@ -45,7 +45,7 @@ export const useMulticall = () => {
* @param contractAddress
* @returns Promise<{ [address: string]: number }>
*/
- const getERC20Balances = async (
+ const getErc20Balances = async (
addresses: Address[],
rpc: string,
contractAddress: Address,
@@ -75,5 +75,5 @@ export const useMulticall = () => {
);
};
- return { getETHBalances, getERC20Balances };
+ return { getEthBalances, getErc20Balances };
};
diff --git a/frontend/hooks/useTabs.tsx b/frontend/hooks/useTabs.tsx
index 2171c0ad4..00179ebaa 100644
--- a/frontend/hooks/useTabs.tsx
+++ b/frontend/hooks/useTabs.tsx
@@ -3,8 +3,10 @@ import { Tab } from '@/enums';
import { useContext } from 'react';
export const useTabs = () => {
- const { activeTab, setActiveTab } = useContext(TabsContext);
+ const { activeTab, setActiveTab: setTab } = useContext(TabsContext);
const resetTabs = () => setActiveTab(Tab.YOUR_AGENTS);
+ const setActiveTab = (tab: string) => setTab(tab as Tab); // casting needed to meet context useState requirements
+
return { activeTab, setActiveTab, resetTabs };
};
diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx
index c8767fd5c..681077e59 100644
--- a/frontend/pages/index.tsx
+++ b/frontend/pages/index.tsx
@@ -4,27 +4,23 @@ import { YourAgents } from '@/components/YourAgents/YourAgents';
import { Tab } from '@/enums';
import { useTabs } from '@/hooks/useTabs';
import { Tabs, type TabsProps } from 'antd';
-import { useMemo } from 'react';
+
+const tabs: TabsProps['items'] = [
+ {
+ key: Tab.YOUR_AGENTS,
+ label: 'Your Agents',
+ children: ,
+ },
+ {
+ key: Tab.MARKETPLACE,
+ label: 'Marketplace',
+ children: ,
+ },
+];
export default function Home() {
const { activeTab, setActiveTab } = useTabs();
- const tabs: TabsProps['items'] = useMemo(
- () => [
- {
- key: Tab.YOUR_AGENTS,
- label: 'Your Agents',
- children: ,
- },
- {
- key: Tab.MARKETPLACE,
- label: 'Marketplace',
- children: ,
- },
- ],
- [],
- );
-
return (