I am trying to run a method from my smart contract located on the ropsten test network but am running into the following error in my getBalance() function:
Unhandled Runtime Error TypeError: Cannot read properties of undefined (reading 'methods')
46 | async function getBalance() {
> 47 | const tempBal = await window.contract.methods.balanceOf(account.data).call()
| ^
48 | setBalance(tempBal)
49 | }
Connecting my contract:
export default function ConnectContract() {
async function loadWeb3() {
if (window.ethereum) {
window.web3 = new Web3(window.ethereum)
window.ethereum.enable()
}
}
async function loadContract() {
const tempContract = await new window.web3.eth.Contract(ABI, ContractAddress)
return tempContract
}
async function load() {
await loadWeb3();
window.contract = await loadContract();
}
load();
}
Currently the functions are being called like so:
export default function Page() {
ConnectContract();
getBalance();
}
Another way I previous called it that did not result in the undefined error was by using an onClick function inside a button to getBalance:
<div><button onClick={() => getBalance}>Get Balance</button></div>
I know calling them like this is causing the getBalance function method to be called before the contract is connected to the website but with the current code, it works after 1-page refresh. But I have not been able to find a solution to make the contract be loaded so the method is defined by the time getBalance is called.
I have tried doing onLoad function and window.load() functions with no success. I have also tried calling my ConnectContract function in the _app.js to load it when the website is launched but that had no success as well.
You have to call the loadWeb3
function inside useEffect
hook. useEffect
is used to prepare the state for the component when it is mounted. to keep track of the state, use useState
hook
const [web3,setWeb3]=useState('')
const [contract,setContract]=useState('')
async function loadWeb3() {
if (window.ethereum) {
window.ethereum.enable()
setWeb3(new Web3(window.ethereum))
}
}
// [] array means we want this useEffect code run only when the compoenent rerenders
// Since loadWeb3 is async function, call it inside try/catch block
useEffect(()=>{loadWeb3()},[])
Next we you need to prepare contract same way.
async function loadContract() {
const tempContract = await new web3.eth.Contract(ABI, ContractAddress)
setContract(tempContract)
}
You also need to call this in useEffect
but this time its dependencies are different. Because in order to get the contract, we are relying on web3
// Since loadContract is async function, call it inside try/catch block
useEffect(()=>{loadContract},[web3])
when component renders, first useEffect will run and set the web3
. Since web3 is changed, component will rerender again this useEffect will run. So when your component is mounted, you will have web3
and contract
set so you can use those.