I'm new to web3 development and am getting the following error in 2 places.
Error 1)
Argument of type '(accounts: string[]) => void' is not assignable to parameter of type '(...args: unknown[]) => void'.
Types of parameters 'accounts' and 'args' are incompatible.
Type 'unknown' is not assignable to type 'string[]'.
Error 2)
Argument of type '(network: string) => void' is not assignable to parameter of type '(...args: unknown[]) => void'.
Types of parameters 'network' and 'args' are incompatible.
Type 'unknown' is not assignable to type 'string'.ts(2345)
Code
import { BrowserProvider, ethers, JsonRpcSigner} from "ethers";
import { useToast } from "@chakra-ui/react";
import { useCallback, useEffect, useState } from "react";
import { MetaMaskInpageProvider } from "@metamask/providers";
declare global {
interface Window{
ethereum?:MetaMaskInpageProvider
}
}
export interface IWeb3State {
address: string | null;
currentChain: number | null;
signer: JsonRpcSigner | null;
provider: BrowserProvider | null;
isAuthenticated: boolean;
}
const useWeb3Provider = () => {
const initialWeb3State = {
address: null,
currentChain: null,
signer: null,
provider: null,
isAuthenticated: false,
};
const toast = useToast();
const [state, setState] = useState<IWeb3State>(initialWeb3State);
const connectWallet = useCallback(async () => {
if (state.isAuthenticated) return;
try {
const { ethereum } = window;
if (!ethereum) {
return toast({
status: "error",
position: "top-right",
title: "Error",
description: "No ethereum wallet found",
});
}
const provider = new ethers.BrowserProvider(ethereum);
const accounts: string[] = await provider.send("eth_requestAccounts", []) as string[];
if (accounts.length > 0) {
const signer = await provider.getSigner();
const chain = Number(await (await provider.getNetwork()).chainId);
//once we have the wallet, are we exporting the variables "provider" & accounts outside this try?
setState({
...state,
address: accounts[0],
signer,
currentChain: chain,
provider,
isAuthenticated: true,
});
localStorage.setItem("isAuthenticated", "true");
}
} catch {}
}, [state, toast]);
const disconnect = () => {
setState(initialWeb3State);
localStorage.removeItem("isAuthenticated");
};
useEffect(() => {
if (window == null) return;
if (localStorage.hasOwnProperty("isAuthenticated")) {
connectWallet();
}
}, [connectWallet, state.isAuthenticated]);
useEffect(() => {
if (typeof window.ethereum === "undefined") return;
window.ethereum.on("accountsChanged", (accounts: string[]) => {
setState({ ...state, address: accounts[0] });
});
window.ethereum.on("networkChanged", (network: string) => {
setState({ ...state, currentChain: network });
});
return () => {
window.ethereum?.removeAllListeners();
};
}, [state]);
return {
connectWallet,
disconnect,
state,
};
};
export default useWeb3Provider;
If it helps, I'm following this tutorial and have been trouble shooting for the past 3 days. Insert crying emoji
I'm not particularly sure how to even ask this question so if any further clarification is needed, just holler!
This doesn't have much to do with web3. It's more of a typescript question. See fixes below with comments:
import { BrowserProvider, ethers, JsonRpcSigner } from "ethers";
import { useToast } from "@chakra-ui/react";
import { useCallback, useEffect, useState } from "react";
import { MetaMaskInpageProvider } from "@metamask/providers";
declare global {
interface Window {
ethereum?: MetaMaskInpageProvider;
}
}
export interface IWeb3State {
address: unknown; // Fix: Change from string | null; ❌
currentChain: unknown; // Fix: Change from number | null; ❌
signer: JsonRpcSigner | null;
provider: BrowserProvider | null;
isAuthenticated: boolean;
}
const useWeb3Provider = () => {
const initialWeb3State = {
address: null,
currentChain: null,
signer: null,
provider: null,
isAuthenticated: false,
};
const toast = useToast();
const [state, setState] = useState<IWeb3State>(initialWeb3State);
const connectWallet = useCallback(async () => {
if (state.isAuthenticated) return;
try {
const { ethereum } = window;
if (!ethereum) {
return toast({
status: "error",
position: "top-right",
title: "Error",
description: "No ethereum wallet found",
});
}
const provider = new ethers.BrowserProvider(ethereum);
const accounts: string[] = (await provider.send("eth_requestAccounts", [])) as string[];
if (accounts.length > 0) {
const signer = await provider.getSigner();
// unnecessary await here ❌
const chain = Number((await provider.getNetwork()).chainId);
//once we have the wallet, are we exporting the variables "provider" & accounts outside this try?
setState({
...state,
address: accounts[0],
signer,
currentChain: chain,
provider,
isAuthenticated: true,
});
localStorage.setItem("isAuthenticated", "true");
}
// Empty block statement is also an issue ❌
} catch (e) {
console.error(e);
}
}, [state, toast]);
const disconnect = () => {
setState(initialWeb3State);
localStorage.removeItem("isAuthenticated");
};
useEffect(() => {
if (window == null) return;
// Do not access Object.prototype method 'hasOwnProperty' from target object. (Fix Below) ❌
if (Object.prototype.hasOwnProperty.call(localStorage, "isAuthenticated")) {
connectWallet();
}
}, [connectWallet, state.isAuthenticated]);
useEffect(() => {
if (typeof window.ethereum === "undefined") return;
// '(...args: unknown[]) => void'. (Fix Below and in IWeb3State above) - literaaly telling you the expected type in the error message ❌
window.ethereum.on("accountsChanged", (...accounts: unknown[]) => {
setState({ ...state, address: accounts[0] });
});
// '(...args: unknown[]) => void'. (Fix Below and in IWeb3State above) - literaaly telling you the expected type in the error message ❌
window.ethereum.on("networkChanged", (network: unknown) => {
setState({ ...state, currentChain: network });
});
return () => {
window.ethereum?.removeAllListeners();
};
}, [state]);
return {
connectWallet,
disconnect,
state,
};
};
export default useWeb3Provider;
An alternative fix would be to use typescript generics:
Here:
// Alternative fix with the use of typescript generics
export interface IWeb3State<T> {
address: T;
currentChain: T;
signer: JsonRpcSigner | null;
provider: BrowserProvider | null;
isAuthenticated: boolean;
}
And here:
// pass unknown here as a type argument to IWeb3State
const [state, setState] = useState<IWeb3State<unknown>>(initialWeb3State);