Skip to content

Commit

Permalink
feat: update behavior to switch chains
Browse files Browse the repository at this point in the history
  • Loading branch information
therealemjy committed Feb 20, 2025
1 parent 84a83d4 commit d5ae865
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 185 deletions.
37 changes: 3 additions & 34 deletions apps/evm/src/containers/ConnectWallet/index.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,24 @@
import { chainMetadata } from '@venusprotocol/chains';
import { useMemo } from 'react';

import { Button, type Variant } from 'components/Button';
import { Button } from 'components/Button';
import { NoticeInfo } from 'components/Notice';
import { useTranslation } from 'libs/translations';
import { useAccountAddress, useAuthModal, useChainId, useSwitchChain } from 'libs/wallet';
import type { ChainId } from 'types';
import { useAccountAddress, useAuthModal } from 'libs/wallet';
import { Container } from './Container';

export interface ConnectWalletProps extends React.HTMLAttributes<HTMLDivElement> {
chainId?: ChainId;
buttonVariant?: Variant;
message?: string;
className?: string;
children?: React.ReactNode;
}

export const ConnectWallet: React.FC<ConnectWalletProps> = ({
children,
chainId,
message,
buttonVariant,
...otherProps
}) => {
const { accountAddress } = useAccountAddress();
const isUserConnected = !!accountAddress;

const { chainId: currentChainId } = useChainId();

const isOnWrongChain = useMemo(
() => chainId && currentChainId !== chainId,
[currentChainId, chainId],
);

const chain = chainId && chainMetadata[chainId];

const { openAuthModal } = useAuthModal();
const { switchChain } = useSwitchChain();
const handleSwitchChain = () => chainId && switchChain({ chainId });

const { t } = useTranslation();

Expand All @@ -46,24 +27,12 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = ({
<Container {...otherProps}>
{!!message && <NoticeInfo className="mb-8" description={message} />}

<Button variant={buttonVariant} className="w-full" onClick={openAuthModal}>
<Button className="w-full" onClick={openAuthModal}>
{t('connectWallet.connectButton')}
</Button>
</Container>
);
}

if (isOnWrongChain) {
return (
<Container {...otherProps}>
<Button variant={buttonVariant} className="w-full" onClick={handleSwitchChain}>
{t('connectWallet.switchChain', {
chainName: chain?.name,
})}
</Button>
</Container>
);
}

return <Container {...otherProps}>{children}</Container>;
};
2 changes: 1 addition & 1 deletion apps/evm/src/containers/Form/RhfSubmitButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const RhfSubmitButton: React.FC<RhfSubmitButtonProps> = ({
}

if (requiresConnectedWallet || spendingApproval) {
dom = <ConnectWallet buttonVariant="primary">{dom}</ConnectWallet>;
dom = <ConnectWallet>{dom}</ConnectWallet>;
}

return <div className={className}>{dom}</div>;
Expand Down
41 changes: 41 additions & 0 deletions apps/evm/src/containers/SwitchChain/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { chainMetadata } from '@venusprotocol/chains';

import { Button } from 'components/Button';
import { useTranslation } from 'libs/translations';
import { useChainId, useSwitchChain } from 'libs/wallet';
import type { ChainId } from 'types';
import { useAccount } from 'wagmi';

// TODO: add tests

export interface SwitchChainProps extends React.HTMLAttributes<HTMLDivElement> {
chainId?: ChainId;
}

export const SwitchChain: React.FC<SwitchChainProps> = ({ children, chainId, ...otherProps }) => {
const { chainId: currentChainId } = useChainId();
const targetChainId = chainId || currentChainId;

const { chainId: walletChainId } = useAccount();
const isOnWrongChain = walletChainId !== targetChainId;
const targetChain = chainMetadata[targetChainId];

const { switchChain } = useSwitchChain();
const { t } = useTranslation();

const handleSwitchChain = () => switchChain({ chainId: targetChainId });

return (
<div {...otherProps}>
{isOnWrongChain ? (
<Button className="w-full" onClick={handleSwitchChain}>
{t('connectWallet.switchChain', {
chainName: targetChain.name,
})}
</Button>
) : (
children
)}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
import { getAccount, watchAccount } from '@wagmi/core';
import { getAccount } from '@wagmi/core';
import { useEffect, useRef } from 'react';
import { useLocation } from 'react-router';
import { useSearchParams } from 'react-router-dom';
import { useDisconnect } from 'wagmi';

import { routes } from 'constants/routing';
import { useNavigate } from 'hooks/useNavigate';
import { getUnsafeChainIdFromSearchParams } from 'libs/wallet';
import config from 'libs/wallet/Web3Wrapper/config';
import { chains, defaultChain } from 'libs/wallet/chains';
import { CHAIN_ID_SEARCH_PARAM } from 'libs/wallet/constants';
import { useUpdateUrlChainId } from 'libs/wallet/hooks/useUpdateUrlChainId';
import { getChainId } from 'libs/wallet/utilities/getChainId';
import config from '../../config';

export const AuthHandler: React.FC = () => {
const { disconnect } = useDisconnect();
const { updateUrlChainId } = useUpdateUrlChainId();
const { navigate } = useNavigate();

const location = useLocation();
const initialLocationRef = useRef(location);
const navigateRef = useRef(navigate);
const [searchParams] = useSearchParams();
const isInitializedRef = useRef(false);

// Initialize wallet connection on mount
useEffect(() => {
Expand All @@ -45,65 +41,25 @@ export const AuthHandler: React.FC = () => {
}
}, []);

// Detect change of chain ID triggered from wallet
useEffect(() => {
const stopWatchingNetwork = watchAccount(config, {
onChange: async ({ chain: walletChain }) => {
if (!walletChain) {
disconnect();
return;
}

const { chainId } = getChainId();

// Disconnect wallet if it is connected to a different chain than the one set in the URL.
// This check is only performed on initial mount, in order to support links without
// redirecting users
if (walletChain.id !== chainId && !isInitializedRef.current) {
disconnect();
}

// Update URL when wallet connects to a different chain
if (walletChain.id !== chainId && isInitializedRef.current) {
updateUrlChainId({ chainId: walletChain.id });
}

if (!isInitializedRef.current) {
isInitializedRef.current = true;
}
},
});

return stopWatchingNetwork;
}, [updateUrlChainId, disconnect]);

// Detect change of chain ID triggered from URL
useEffect(() => {
const fn = async () => {
const { chainId: unsafeSearchParamChainId } = getUnsafeChainIdFromSearchParams({
searchParams,
});

const { chain: walletChain } = getAccount(config);

// Update URL again if it was updated with an unsupported chain ID or does not contain any
// chain ID search param
// Update URL if it was updated with an unsupported chain ID or does not contain any chain ID
// search param
if (
unsafeSearchParamChainId === undefined ||
!chains.some(chain => chain.id === unsafeSearchParamChainId)
) {
updateUrlChainId({ chainId: walletChain?.id ?? defaultChain.id });
return;
}

// Disconnect wallet if chain ID changed in URL but wallet is connected to a different network
if (walletChain && walletChain.id !== unsafeSearchParamChainId) {
disconnect();
updateUrlChainId({ chainId: defaultChain.id });
}
};

fn();
}, [searchParams, updateUrlChainId, disconnect]);
}, [searchParams, updateUrlChainId]);

return null;
};
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,7 @@ export const BorrowFormUi: React.FC<BorrowFormUiProps> = ({
<div className="space-y-6">
<AssetInfo asset={asset} action="borrow" />

<ConnectWallet buttonVariant="primary">
{t('operationForm.connectWalletButtonLabel')}
</ConnectWallet>
<ConnectWallet>{t('operationForm.connectWalletButtonLabel')}</ConnectWallet>
</div>
)}
</form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,9 +378,7 @@ export const RepayFormUi: React.FC<RepayFormUiProps> = ({
<div className="space-y-6">
<AssetInfo asset={asset} action="repay" />

<ConnectWallet buttonVariant="primary">
{t('operationForm.connectWalletButtonLabel')}
</ConnectWallet>
<ConnectWallet>{t('operationForm.connectWalletButtonLabel')}</ConnectWallet>
</div>
)}
</form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useTranslation } from 'libs/translations';
import type { Swap, Token } from 'types';
import { cn } from 'utilities';

import { SwitchChain } from 'containers/SwitchChain';
import SwapSummary from '../../SwapSummary';
import type { FormError } from '../../types';
import type { FormErrorCode } from '../useForm/types';
Expand Down Expand Up @@ -57,17 +58,8 @@ export const SubmitSection: React.FC<SubmitSectionProps> = ({
return t('operationForm.submitButtonLabel.supply');
}, [isFormValid, t]);

return (
<ApproveTokenSteps
token={fromToken}
isUsingSwap={isUsingSwap}
hideTokenEnablingStep={!isFormValid}
isTokenApproved={isFromTokenApproved}
approveToken={approveFromToken}
isApproveTokenLoading={isApproveFromTokenLoading}
isWalletSpendingLimitLoading={isFromTokenWalletSpendingLimitLoading}
secondStepButtonLabel={submitButtonLabel}
>
const dom = (
<>
<PrimaryButton
type="submit"
loading={isFormSubmitting}
Expand All @@ -87,8 +79,28 @@ export const SubmitSection: React.FC<SubmitSectionProps> = ({
{isFormValid && !isSwapLoading && !isFromTokenWalletSpendingLimitLoading && (
<SwapSummary swap={swap} type="supply" />
)}
</ApproveTokenSteps>
</>
);

if (isFormValid) {
return (
<SwitchChain>
<ApproveTokenSteps
token={fromToken}
isUsingSwap={isUsingSwap}
isTokenApproved={isFromTokenApproved}
approveToken={approveFromToken}
isApproveTokenLoading={isApproveFromTokenLoading}
isWalletSpendingLimitLoading={isFromTokenWalletSpendingLimitLoading}
secondStepButtonLabel={submitButtonLabel}
>
{dom}
</ApproveTokenSteps>
</SwitchChain>
);
}

return dom;
};

export default SubmitSection;
Loading

0 comments on commit d5ae865

Please sign in to comment.