diff --git a/src/components/buttons/ConnectAwareSubmitButton.tsx b/src/components/buttons/ConnectAwareSubmitButton.tsx index 91e039f..46d8b27 100644 --- a/src/components/buttons/ConnectAwareSubmitButton.tsx +++ b/src/components/buttons/ConnectAwareSubmitButton.tsx @@ -1,34 +1,38 @@ -import { ProtocolType } from '@hyperlane-xyz/utils'; -import { useAccountForChain, useConnectFns, useTimeout } from '@hyperlane-xyz/widgets'; +import { useAccounts, useConnectFns, useTimeout } from '@hyperlane-xyz/widgets'; import { useFormikContext } from 'formik'; -import { useCallback } from 'react'; -import { useChainProtocol, useMultiProvider } from '../../features/chains/hooks'; +import { useCallback, useMemo } from 'react'; +import { useMultiProvider } from '../../features/chains/hooks'; import { SolidButton } from './SolidButton'; interface Props { - chainName: ChainName; + chains: ChainName[]; text: string; - classes?: string; + className?: string; } -export function ConnectAwareSubmitButton({ chainName, text, classes }: Props) { - const protocol = useChainProtocol(chainName) || ProtocolType.Ethereum; +export function ConnectAwareSubmitButton({ chains, text, className }: Props) { + const multiProvider = useMultiProvider(); + const { accounts } = useAccounts(multiProvider); const connectFns = useConnectFns(); - const connectFn = connectFns[protocol]; - const multiProvider = useMultiProvider(); - const account = useAccountForChain(multiProvider, chainName); - const isAccountReady = account?.isReady; + const unconnectedProtocols = useMemo(() => { + const protocols = new Set(chains.map((c) => multiProvider.getProtocol(c))); + return [...protocols.values().filter((p) => !accounts[p]?.isReady)]; + }, [accounts, chains, multiProvider]); + const isAccountsReady = unconnectedProtocols.length === 0; const { errors, setErrors, touched, setTouched } = useFormikContext(); const hasError = Object.keys(touched).length > 0 && Object.keys(errors).length > 0; - const firstError = `${Object.values(errors)[0]}` || 'Unknown error'; const color = hasError ? 'red' : 'accent'; - const content = hasError ? firstError : isAccountReady ? text : 'Connect wallet'; - const type = isAccountReady ? 'submit' : 'button'; - const onClick = isAccountReady ? undefined : connectFn; + const type = isAccountsReady ? 'submit' : 'button'; + const onClick = isAccountsReady ? undefined : connectFns[unconnectedProtocols[0]]; + + let content; + if (hasError) content = 'Error'; + else if (isAccountsReady) content = text; + else content = `Connect wallet${unconnectedProtocols.length > 1 ? 's' : ''}`; // Automatically clear error state after a timeout const clearErrors = useCallback(() => { @@ -40,7 +44,7 @@ export function ConnectAwareSubmitButton({ chainName, text, cl useTimeout(clearErrors, 3500); return ( - + {content} ); diff --git a/src/features/chains/ChainSelectModal.tsx b/src/features/chains/ChainSelectModal.tsx index 7d2788a..e921860 100644 --- a/src/features/chains/ChainSelectModal.tsx +++ b/src/features/chains/ChainSelectModal.tsx @@ -31,7 +31,6 @@ export function ChainSelectListModal({ onClickChain={onSelectChain} overrideChainMetadata={chainMetadataOverrides} onChangeOverrideMetadata={setChainMetadataOverrides} - defaultSortField="custom" showChainDetails={showChainDetails} /> diff --git a/src/features/chains/metadata.ts b/src/features/chains/metadata.ts index 61a5339..27b3151 100644 --- a/src/features/chains/metadata.ts +++ b/src/features/chains/metadata.ts @@ -5,7 +5,7 @@ import { ChainMetadataSchema, mergeChainMetadataMap, } from '@hyperlane-xyz/sdk'; -import { objMap, promiseObjAll } from '@hyperlane-xyz/utils'; +import { objFilter, objMap, promiseObjAll, ProtocolType } from '@hyperlane-xyz/utils'; import { z } from 'zod'; import { chains as ChainsTS } from '../../consts/chains.ts'; import ChainsYaml from '../../consts/chains.yaml'; @@ -47,7 +47,13 @@ export async function assembleChainMetadata( ), ); - const chainMetadata = mergeChainMetadataMap(registryChainMetadata, filesystemMetadata); + // TODO remove if/when this app isn't EVM-only + const evmRegistryChainMetadata = objFilter( + registryChainMetadata, + (_, m): m is ChainMetadata => m.protocol === ProtocolType.Ethereum, + ); + + const chainMetadata = mergeChainMetadataMap(evmRegistryChainMetadata, filesystemMetadata); const chainMetadataWithOverrides = mergeChainMetadataMap(chainMetadata, storeMetadataOverrides); return { chainMetadata, chainMetadataWithOverrides }; } diff --git a/src/features/deployment/warp/WarpDeploymentForm.tsx b/src/features/deployment/warp/WarpDeploymentForm.tsx index a4b250d..da001c9 100644 --- a/src/features/deployment/warp/WarpDeploymentForm.tsx +++ b/src/features/deployment/warp/WarpDeploymentForm.tsx @@ -4,6 +4,7 @@ import { errorToString, ProtocolType } from '@hyperlane-xyz/utils'; import { AccountInfo, Button, IconButton, useAccounts, XIcon } from '@hyperlane-xyz/widgets'; import { Form, Formik, useFormikContext } from 'formik'; import Image from 'next/image'; +import { useMemo } from 'react'; import { BackButton } from '../../../components/buttons/BackButton'; import { ConnectAwareSubmitButton } from '../../../components/buttons/ConnectAwareSubmitButton'; import { TextInput } from '../../../components/input/TextField'; @@ -58,7 +59,7 @@ export function WarpDeploymentForm() { {() => (
-
+
@@ -174,15 +175,14 @@ function AddConfigButton() { function ButtonSection() { const { values } = useFormikContext(); - + const chains = useMemo(() => values.configs.map((c) => c.chainName), [values]); return (
- {/* // TODO check for all chains, not just one */} - + chains={chains} text="Continue" - classes="px-3 py-1.5" + className="px-3 py-1.5" />
);