diff --git a/packages/frontend/cypress/e2e/full/step1.cy.ts b/packages/frontend/cypress/e2e/full/step1.cy.ts index f659b3d..a2fc508 100644 --- a/packages/frontend/cypress/e2e/full/step1.cy.ts +++ b/packages/frontend/cypress/e2e/full/step1.cy.ts @@ -22,6 +22,7 @@ describe('Multistep form step-1 with Topos', () => { cy.get('#sendingSubnet').click() cy.get('.ant-select-item-option-content').contains('Topos').click() cy.get('#nextButton').click() + cy.wait(500) }) it('should have token field enabled and others disabled', () => { diff --git a/packages/frontend/src/components/steps/Step0.tsx b/packages/frontend/src/components/steps/Step0.tsx index b8a46e2..c6a99c4 100644 --- a/packages/frontend/src/components/steps/Step0.tsx +++ b/packages/frontend/src/components/steps/Step0.tsx @@ -23,7 +23,7 @@ const Step0 = ({ onFinish }: StepProps) => { const { status } = useEthers({ subnet: sendingSubnet, - viaMetaMask: sendingSubnet !== undefined, + viaMetaMask: true, }) const nextStep = useCallback(() => { diff --git a/packages/frontend/src/hooks/useEthers.ts b/packages/frontend/src/hooks/useEthers.ts index 0900b42..7ac7b9f 100644 --- a/packages/frontend/src/hooks/useEthers.ts +++ b/packages/frontend/src/hooks/useEthers.ts @@ -1,8 +1,9 @@ import { BrowserProvider, getDefaultProvider } from 'ethers' -import { useEffect, useMemo } from 'react' +import { useContext, useEffect, useMemo } from 'react' import { useMetaMask } from 'metamask-react' import { Subnet } from '../types' +import { ErrorsContext } from '../contexts/errors' interface Args { subnet?: Subnet @@ -10,6 +11,7 @@ interface Args { } export default function useEthers({ subnet, viaMetaMask }: Args = {}) { + const { setErrors } = useContext(ErrorsContext) const { account, addChain, connect, ethereum, status, switchChain } = useMetaMask() @@ -18,16 +20,39 @@ export default function useEthers({ subnet, viaMetaMask }: Args = {}) { return new BrowserProvider(ethereum) } - if (!subnet) { - const toposSubnetEndpointWs = import.meta.env - .VITE_TOPOS_SUBNET_ENDPOINT_WS - return getDefaultProvider(toposSubnetEndpointWs) - } + const endpoint = subnet + ? subnet.endpointWs || subnet.endpointHttp + : import.meta.env.VITE_TOPOS_SUBNET_ENDPOINT_WS - const endpoint = subnet.endpointWs || subnet.endpointHttp return getDefaultProvider(endpoint) }, [subnet, viaMetaMask, ethereum]) + useEffect( + function verifyProviderReadiness() { + const timeoutId = window.setTimeout(() => { + if (!viaMetaMask && !(provider as any).ready) { + setErrors((e) => [ + ...e, + { + message: `Could not reach provider's endpoint${ + ' (' + + (subnet + ? subnet.endpointWs || subnet.endpointHttp + : import.meta.env.VITE_TOPOS_SUBNET_ENDPOINT_WS) + + ')' + }`, + }, + ]) + } + }, 3000) + + return function clearTimeout() { + window.clearTimeout(timeoutId) + } + }, + [provider] + ) + useEffect( function switchNetworkAndConnect() { const _ = async () => { diff --git a/packages/frontend/src/hooks/useRegisteredSubnets.ts b/packages/frontend/src/hooks/useRegisteredSubnets.ts index 7ffa7ee..cc46c1f 100644 --- a/packages/frontend/src/hooks/useRegisteredSubnets.ts +++ b/packages/frontend/src/hooks/useRegisteredSubnets.ts @@ -1,9 +1,9 @@ import { SubnetRegistrator__factory } from '@topos-protocol/topos-smart-contracts/typechain-types' import {} from 'ethers' -import { useCallback, useContext, useEffect, useState } from 'react' +import { useContext, useEffect, useState } from 'react' import { ErrorsContext } from '../contexts/errors' -import { Subnet, SubnetWithId } from '../types' +import { SubnetWithId } from '../types' import useEthers from './useEthers' export default function useRegisteredSubnets() { @@ -12,78 +12,83 @@ export default function useRegisteredSubnets() { const [loading, setLoading] = useState(false) const [registeredSubnets, setRegisteredSubnets] = useState() - const contract = SubnetRegistrator__factory.connect( - import.meta.env.VITE_SUBNET_REGISTRATOR_CONTRACT_ADDRESS, - provider - ) - - const getRegisteredSubnets = useCallback(async () => { - setLoading(true) + useEffect( + function getRegisteredSubnets() { + async function _() { + setLoading(true) - const registeredSubnetsCount = await contract - .getSubnetCount() - .then((count) => Number(count)) - .catch((error: any) => { - console.error(error) - setErrors((e) => [ - ...e, - { message: `Error when fetching the count of registered subnets.` }, - ]) - }) + const subnetRegistrator = SubnetRegistrator__factory.connect( + import.meta.env.VITE_SUBNET_REGISTRATOR_CONTRACT_ADDRESS, + provider + ) - if (registeredSubnetsCount !== undefined) { - const promises = [] - let i = 0 - while (i < registeredSubnetsCount) { - const subnetId = await contract - .getSubnetIdAtIndex(i) + const registeredSubnetsCount = await subnetRegistrator + .getSubnetCount() + .then((count) => Number(count)) .catch((error: any) => { console.error(error) setErrors((e) => [ ...e, { - message: `Error fetching the id of the registered subnet at index ${i}.`, + message: `Error when fetching the count of registered subnets.`, }, ]) }) - if (subnetId !== undefined) { - promises.push( - contract - .subnets(subnetId) - .then((subnet) => ({ - ...(subnet as any).toObject(), // toObject method of ES6 Proxy - id: subnetId, - })) - .catch((error: Error) => { + if (registeredSubnetsCount !== undefined) { + const promises = [] + let i = 0 + while (i < registeredSubnetsCount) { + const subnetId = await subnetRegistrator + .getSubnetIdAtIndex(i) + .catch((error: any) => { console.error(error) setErrors((e) => [ ...e, { - message: `Error fetching registered subnet with id ${subnetId}.`, + message: `Error fetching the id of the registered subnet at index ${i}.`, }, ]) }) + + if (subnetId !== undefined) { + promises.push( + subnetRegistrator + .subnets(subnetId) + .then((subnet) => ({ + ...(subnet as any).toObject(), // toObject method of ES6 Proxy + id: subnetId, + })) + .catch((error: Error) => { + console.error(error) + setErrors((e) => [ + ...e, + { + message: `Error fetching registered subnet with id ${subnetId}.`, + }, + ]) + }) + ) + } + i++ + } + + const subnets = await Promise.allSettled(promises).then((values) => + values + .filter((v) => v.status === 'fulfilled') + .map((v) => (v.status === 'fulfilled' ? v.value : undefined)) + .filter((v) => v && v.name === 'Incal') ) + setRegisteredSubnets(subnets as SubnetWithId[]) } - i++ - } - - const subnets = await Promise.allSettled(promises).then((values) => - values - .filter((v) => v.status === 'fulfilled') - .map((v) => (v.status === 'fulfilled' ? v.value : undefined)) - .filter((v) => v && v.name === 'Incal') - ) - setRegisteredSubnets(subnets as SubnetWithId[]) - } - setLoading(false) - }, []) + setLoading(false) + } - useEffect(function init() { - getRegisteredSubnets() - }, []) + _() + }, + [provider] + ) return { loading, registeredSubnets } }