Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added switch network method #22

Merged
merged 7 commits into from
Jan 20, 2022
Merged
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
11 changes: 9 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
{
"extends": ["next/core-web-vitals", "prettier", "plugin:import/recommended", "plugin:import/typescript", "plugin:@typescript-eslint/recommended"],
"extends": [
"next/core-web-vitals",
"prettier",
"plugin:import/recommended",
"plugin:import/typescript",
"plugin:@typescript-eslint/recommended"
],
"plugins": ["prettier", "import", "simple-import-sort", "@typescript-eslint"],
"rules": {
"@typescript-eslint/no-empty-interface": "off",
Expand All @@ -13,6 +19,7 @@
"import/newline-after-import": "error",
"import/no-duplicates": "error",
"import/no-named-as-default": "error",
"import/no-unresolved": "warn"
"import/no-unresolved": "warn",
"@next/next/no-img-element": "off"
}
}
7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,16 @@
"@walletconnect/web3-provider": "^1.7.1",
"eth-provider": "^0.10.0",
"ethereum-blockies-base64": "^1.0.2",
"react-intl": "^5.24.3",
"walletlink": "^2.4.0",
"web3modal": "^1.9.5",
"ethers": "^5.5.3",
"graphql": "^16.2.0",
"graphql-ws": "^5.5.5",
"next": "latest",
"react": "latest",
"react-dom": "latest",
"reflect-metadata": "^0.1.13",
"subscriptions-transport-ws": "^0.11.0"
"subscriptions-transport-ws": "^0.11.0",
"walletlink": "^2.4.0",
"web3modal": "^1.9.5"
},
"devDependencies": {
"@babel/core": "^7.16.7",
Expand Down
27 changes: 17 additions & 10 deletions src/layouts/WalletWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,15 @@ import RemoveCircleOutlineRoundedIcon from '@mui/icons-material/RemoveCircleOutl
import { Button, Divider, ListItemIcon, ListItemText } from '@mui/material';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import { useTheme } from '@mui/system';
import makeBlockie from 'ethereum-blockies-base64';
import React, { useEffect, useState } from 'react';
import useGetEns from 'src/libs/hooks/use-get-ens';
import { useWeb3Context } from 'src/libs/web3-data-provider/Web3ContextProvider';
import { getNetworkConfig } from 'src/utils/marketsAndNetworksConfig';

import { ColorModeContext } from './AppGlobalStyles';

export default function WalletWidget() {
const { connectWallet, disconnectWallet, currentAccount, connected, chainId } = useWeb3Context();
const { connectWallet, disconnectWallet, currentAccount, connected, chainId, switchNetwork } =
useWeb3Context();

const { name: ensName, avatar: ensAvatar } = useGetEns(currentAccount);
const ensNameAbbreviated = ensName
Expand All @@ -25,10 +23,7 @@ export default function WalletWidget() {
: ensName
: undefined;

const theme = useTheme();
const colorMode = React.useContext(ColorModeContext);

const [anchorEl, setAnchorEl] = useState(null);
const [anchorEl, setAnchorEl] = useState<Element | null>(null);
const [useBlockie, setUseBlockie] = useState(false);

useEffect(() => {
Expand All @@ -41,7 +36,7 @@ export default function WalletWidget() {

const networkConfig = getNetworkConfig(chainId);

const handleClick = (event: { currentTarget: React.SetStateAction<null> }) => {
const handleClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
if (!connected) {
connectWallet();
} else {
Expand Down Expand Up @@ -72,6 +67,11 @@ export default function WalletWidget() {
setAnchorEl(null);
};

const handleSwitchNetwork = () => {
switchNetwork(137);
setAnchorEl(null);
};

return (
<div>
<Button
Expand All @@ -82,7 +82,7 @@ export default function WalletWidget() {
aria-controls={open ? 'more-menu' : undefined}
aria-expanded={open ? 'true' : undefined}
aria-haspopup="true"
onClick={handleClick}
onClick={(event) => handleClick(event)}
color="inherit"
startIcon={
connected ? (
Expand Down Expand Up @@ -144,6 +144,13 @@ export default function WalletWidget() {
</ListItemIcon>
<ListItemText>Disconnect Wallet</ListItemText>
</MenuItem>
<Divider />
<MenuItem onClick={handleSwitchNetwork}>
<ListItemIcon>
<RemoveCircleOutlineRoundedIcon fontSize="small" />
</ListItemIcon>
<ListItemText>SwitchNetwork</ListItemText>
</MenuItem>
</Menu>
</div>
);
Expand Down
75 changes: 66 additions & 9 deletions src/libs/web3-data-provider/Web3ContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { JsonRpcProvider, Network, Web3Provider } from '@ethersproject/providers';
import { providers } from 'ethers';
import React, { ReactElement, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { getNetworkConfig } from 'src/utils/marketsAndNetworksConfig';
import Web3Modal from 'web3modal';

export type Web3Data = {
Expand All @@ -11,6 +12,7 @@ export type Web3Data = {
provider: JsonRpcProvider | undefined;
web3Modal: Web3Modal;
chainId: number;
switchNetwork: (chainId: number) => Promise<void>;
};

export type Web3ContextData = {
Expand All @@ -31,23 +33,21 @@ export const useWeb3Context = () => {
const { web3ProviderData } = web3Context;
return useMemo<Web3Data>(() => {
return { ...web3ProviderData };
}, [web3Context]);
}, [web3ProviderData]);
};

export const Web3ContextProvider: React.FC<{ children: ReactElement }> = ({ children }) => {
const [provider, setProvider] = useState<JsonRpcProvider>();
const [connected, setConnected] = useState(false);
const [chainId, setChainId] = useState(1);
const [currentAccount, setCurrentAccount] = useState('');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [web3Provider, setWeb3Provider] = useState(undefined as any);

const [web3Modal, setWeb3Modal] = useState<Web3Modal>(undefined as unknown as Web3Modal);

useEffect(() => {
if (web3Modal?.cachedProvider) connectWallet();
}, [web3Modal]);

useEffect(() => {
import('./modalOptions').then((m) => setWeb3Modal(m.getWeb3Modal(chainId)));
import('./modalOptions').then((m) => setWeb3Modal(m.getWeb3Modal()));
}, [chainId, currentAccount]);

// provider events subscriptions
Expand Down Expand Up @@ -75,6 +75,7 @@ export const Web3ContextProvider: React.FC<{ children: ReactElement }> = ({ chil
// web 3 modal
const connectWallet = useCallback(async () => {
const providerInstance = await web3Modal.connect();
setWeb3Provider(providerInstance);
await initSubscriptions(providerInstance);

const ethProvider = new providers.Web3Provider(providerInstance);
Expand All @@ -91,13 +92,59 @@ export const Web3ContextProvider: React.FC<{ children: ReactElement }> = ({ chil
setConnected(true);

return ethProvider;
}, [provider, web3Modal, connected]);
}, [web3Modal, initSubscriptions]);

const disconnectWallet = useCallback(async () => {
web3Modal.clearCachedProvider();
setConnected(false);
setCurrentAccount('');
}, [provider, web3Modal, connected]);
if (web3Provider) {
if (web3Provider.close) {
await web3Provider.close();
} else if (web3Provider.disconnect) {
web3Provider.disconnect();
} else {
console.log('provider: ', web3Provider);
}
}
}, [web3Modal, web3Provider]);

const switchNetwork = useCallback(
async (newChainId: number) => {
if (provider) {
try {
await provider.send('wallet_switchEthereumChain', [
{ chainId: `0x${newChainId.toString(16)}` },
]);
} catch (switchError) {
console.log(switchError);
const networkInfo = getNetworkConfig(newChainId);
// @ts-expect-error to correctly type we should add a conditional here to check instanceof Error
if (switchError.code === 4902) {
try {
await provider.send('wallet_addEthereumChain', [
{
chainId: `0x${newChainId.toString(16)}`,
chainName: networkInfo.name,
nativeCurrency: networkInfo.baseAssetSymbol,
rpcUrls: [...networkInfo.publicJsonRPCUrl, networkInfo.publicJsonRPCWSUrl],
blockExplorerUrls: networkInfo.explorerLink,
},
]);
} catch (addError) {
console.log(addError);
// TODO: handle error somehow
}
}
}
}
},
[provider]
);

useEffect(() => {
if (web3Modal?.cachedProvider) connectWallet();
}, [web3Modal, connectWallet]);

const web3ProviderData = useMemo(
() => ({
Expand All @@ -108,8 +155,18 @@ export const Web3ContextProvider: React.FC<{ children: ReactElement }> = ({ chil
currentAccount,
web3Modal,
chainId,
switchNetwork,
}),
[connectWallet, disconnectWallet, provider, connected, currentAccount, web3Modal, chainId]
[
connectWallet,
disconnectWallet,
provider,
connected,
currentAccount,
web3Modal,
chainId,
switchNetwork,
]
);

return (
Expand Down
19 changes: 16 additions & 3 deletions src/libs/web3-data-provider/modalOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ const POLLING_INTERVAL = 12000;
const APP_NAME = 'Aave';
const APP_LOGO_URL = 'https://aave.com/favicon.ico';

export const getWeb3Modal = (networkId: number) => {
export const getWeb3Modal = () => {
const supportedChainIds = getSupportedChainIds();
const networkConfig = getNetworkConfig(networkId);
return new Web3Modal({
cacheProvider: true,
providerOptions: {
Expand All @@ -36,12 +35,26 @@ export const getWeb3Modal = (networkId: number) => {
options: {
appName: APP_NAME,
appLogoUrl: APP_LOGO_URL,
url: networkConfig.privateJsonRPCUrl || networkConfig.publicJsonRPCUrl[0],
rpc: supportedChainIds.reduce((acc, network) => {
const config = getNetworkConfig(network);
acc[network] = config.privateJsonRPCUrl || config.publicJsonRPCUrl[0];
return acc;
}, {} as { [networkId: number]: string }),
},
},
frame: {
package: ethProvider, // required
},
// mewconnect: {
// package: MewConnect, // required
// options: {
// rpc: supportedChainIds.reduce((acc, network) => {
// const config = getNetworkConfig(network);
// acc[network] = config.privateJsonRPCUrl || config.publicJsonRPCUrl[0];
// return acc;
// }, {} as { [networkId: number]: string }),
// },
// },
},
});
};
Loading