Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
"unexpected_network_title": "Unexpected network",
"unexpected_network": "Please connect your wallet to '{{network}}'",
"metamask_not_found": "Missing metamask plugin - get it from",
"wallet_provider_missing": "No browser wallet provider was found.",
"switch_network_failed": "Network switch failed.",
"wallet_connect_failed": "WalletConnect login failed or cancelled",
"field": {
"required": "This field is required",
Expand Down
52 changes: 43 additions & 9 deletions src/components/layout/unexpected_chain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
import { Alert, AlertTitle, Button, Paper } from "@mui/material";
import Box from "@mui/system/Box";
import { useTranslation } from "next-i18next";
import { useState } from "react";
import { toHexString } from "../../utils/numbers";

export default function UnexpectedChain() {
const { t } = useTranslation('common');
const [switchError, setSwitchError] = useState<string>();
const [switching, setSwitching] = useState(false);
const chainName = process.env.NEXT_PUBLIC_CHAIN_NAME;
const chainId = process.env.NEXT_PUBLIC_CHAIN_ID;
const chainRpcUrl = process.env.NEXT_PUBLIC_CHAIN_RPC_URL;
Expand All @@ -16,23 +19,41 @@ export default function UnexpectedChain() {
const allowAutoAdd = chainRpcUrl?.startsWith('https://'); // adding chain only works for https rpc URLs

async function switchNetwork() {
setSwitchError(undefined);
setSwitching(true);

try {
// @ts-ignore
await window.ethereum.request({
const ethereum = (window as any).ethereum;
if (!ethereum) {
throw new Error(t('error.wallet_provider_missing'));
}

await ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: toHexString(chainId ?? '0') }],
});
} catch(switchError) {
// @ts-ignore
if (allowAutoAdd && switchError.code === 4902) {
addNetwork();
if (allowAutoAdd && isUnrecognizedChainError(switchError)) {
try {
await addNetwork();
} catch(addError) {
setSwitchError(getWalletErrorMessage(addError));
}
} else {
setSwitchError(getWalletErrorMessage(switchError));
}
} finally {
setSwitching(false);
}
}

async function addNetwork() {
// @ts-ignore
await window.ethereum.request({
const ethereum = (window as any).ethereum;
if (!ethereum) {
throw new Error(t('error.wallet_provider_missing'));
}

await ethereum.request({
method: 'wallet_addEthereumChain',
params: [
{
Expand All @@ -50,19 +71,32 @@ export default function UnexpectedChain() {
});
}

function isUnrecognizedChainError(error: any): boolean {
return error?.code === 4902 || error?.data?.originalError?.code === 4902;
}

function getWalletErrorMessage(error: any): string {
return error?.data?.originalError?.message ?? error?.message ?? t('error.switch_network_failed');
}

return (
<div>
<Paper elevation={1} >
<Alert severity="warning">
<AlertTitle>{t('error.unexpected_network_title')}</AlertTitle>
{t('error.unexpected_network', { network: chainName})}
<Box sx={{ pt: 2 }}>
<Button variant="contained" color="secondary" onClick={switchNetwork}>
<Button variant="contained" color="secondary" onClick={switchNetwork} disabled={switching}>
{t('action.switch_network', { network: chainName})}
</Button>
</Box>
{switchError && (
<Box sx={{ pt: 2 }}>
<Alert severity="error">{switchError}</Alert>
</Box>
)}
</Alert>
</Paper>
</div>
);
}
}
Loading