Skip to content

Instantly share code, notes, and snippets.

@feliperodriguess
Created October 6, 2021 16:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save feliperodriguess/baee3f274cdf2317bd608b3cac8cb03c to your computer and use it in GitHub Desktop.
Save feliperodriguess/baee3f274cdf2317bd608b3cac8cb03c to your computer and use it in GitHub Desktop.
useMetamaskWallet hook w/ TypeScript
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { ethers } from 'ethers';
declare global {
interface Window {
ethereum: {
on: (method: string, callback?: (arg: any) => void) => Promise<any>;
request: (args: EthereumRequestArguments) => Promise<any>;
removeListener: (method: string, callback?: (arg: any) => void) => Promise<any>;
selectedAddress: string;
};
}
}
export interface EthereumRequestArguments {
method: string;
params?: unknown[] | object;
}
export interface ProviderProps {
children: React.ReactNode;
}
export const METAMASK_STATUS = {
CONNECTED: 'CONNECTED',
DISCONNECTED: 'DISCONNECTED',
INSTALLED: 'INSTALLED',
NOT_INSTALLED: 'NOT_INSTALLED'
};
const INITIAL_CONTEXT_VALUES = {
metamaskStatus: '',
address: '',
provider: {} as ethers.providers.Web3Provider,
signer: {} as ethers.Signer,
connect: () => {}
};
const WalletContext = createContext(INITIAL_CONTEXT_VALUES);
const WalletProvider = ({ children }: ProviderProps) => {
const [metamaskStatus, setMetamaskStatus] = useState('');
const [address, setAddress] = useState('');
const [provider, setProvider] = useState({} as ethers.providers.Web3Provider);
const [signer, setSigner] = useState({} as ethers.Signer);
const connect = useCallback(async () => {
const accountList = await window?.ethereum?.request({ method: 'eth_requestAccounts' });
const hasSomeAccountConnected = accountList?.length > 0;
if (hasSomeAccountConnected) {
setAddress(accountList[0]);
setMetamaskStatus(METAMASK_STATUS.CONNECTED);
const web3Provider = new ethers.providers.Web3Provider(window?.ethereum);
setProvider(web3Provider);
setSigner(web3Provider?.getSigner());
return;
}
setMetamaskStatus(METAMASK_STATUS.DISCONNECTED);
}, []);
const handleAccountsChanged = async (accounts: string[]): Promise<void> => {
const hasSomeAccountConnected = accounts?.length > 0;
if (!hasSomeAccountConnected) {
setAddress('');
setMetamaskStatus(METAMASK_STATUS.DISCONNECTED);
setProvider({} as ethers.providers.Web3Provider);
setSigner({} as ethers.Signer);
return;
}
setAddress(accounts[0]);
const web3Provider = new ethers.providers.Web3Provider(window?.ethereum);
setProvider(web3Provider);
setSigner(web3Provider?.getSigner());
};
useEffect(() => {
(async () => {
const isMetamaskInstalled = typeof window.ethereum !== 'undefined';
const accountList = await window?.ethereum?.request({ method: 'eth_accounts' });
const selectedAddress = accountList?.length > 0 ? accountList[0] : '';
if (isMetamaskInstalled) {
setMetamaskStatus(METAMASK_STATUS.INSTALLED);
if (selectedAddress) {
setAddress(selectedAddress);
setMetamaskStatus(METAMASK_STATUS.CONNECTED);
const web3Provider = new ethers.providers.Web3Provider(window?.ethereum);
setProvider(web3Provider);
setSigner(web3Provider?.getSigner());
}
return;
}
setMetamaskStatus(METAMASK_STATUS.NOT_INSTALLED);
})();
}, []);
useEffect(() => {
window?.ethereum?.on('accountsChanged', handleAccountsChanged);
return () => {
window?.ethereum?.removeListener('accountsChanged', handleAccountsChanged);
};
}, []);
const value = useMemo(
() => ({
address,
connect,
metamaskStatus,
provider,
signer
}),
[address, connect, metamaskStatus, provider, signer]
);
return <WalletContext.Provider value={value}>{children}</WalletContext.Provider>;
};
const useWallet = () => {
const context = useContext(WalletContext);
return context;
};
export { WalletProvider, useWallet };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment