'Web3 error when trying to call contract methods
I encounter the following error while trying to run a dapp made using React, Truffle & Ganache. I'm also using web3 version 1.7.1. The problem is that the code enters in a catch(error) sequence of a try/catch and then displays what it is intended correctly. Why is this happening and why does the following error appears?
index.js:1 TypeError: Cannot read properties of undefined (reading 'methods')
at HomePage.componentDidMount
What the code should do: Display something like "Address is: 0x0D05b3220E9cC7A90623fc506cEB64Ab885FD6C6"
What the code does: It shows me the prompt "Failed to load web3, accounts, or contract. Check console for details" AND THEN "Address is: 0x0D05b3220E9cC7A90623fc506cEB64Ab885FD6C6"
The code is the following:
import React, { Component } from "react";
import MySmartContract from "../contracts/MySmartContract.json";
import getWeb3 from "../getWeb3";
//Importing components
class HomePage extends Component {
constructor(props) {
super(props);
this.state = {
ContractInstance: undefined,
account: null,
web3: null,
isOwner: false
}
}
componentDidMount = async () => {
// For refreshing the page a single time
// so that web3 and instance is loaded every time
if (!window.location.hash) {
window.location = window.location + '#loaded';
window.location.reload();
}
try {
// Get network provider and web3 instance.
const web3 = await getWeb3();
// Use web3 to get the user's accounts.
const accounts = await web3.eth.getAccounts();
// Get the contract instance.
const networkId = await web3.eth.net.getId();
const deployedNetwork = MySmartContract.networks[networkId];
const instance = new web3.eth.Contract(
MySmartContract.abi,
deployedNetwork && deployedNetwork.address,
);
// Set web3, accounts, and contract to the state, and then proceed with an
// example of interacting with the contract's methods.
this.setState({ ContractInstance: instance, web3: web3, account: accounts[0] });
const owner = await this.state.ContractInstance.methods.getOwnerAddress().call();
if (this.state.account === owner) {
this.setState({ isOwner: true });
}
} catch (error) {
// Catch any errors for any of the above operations.
alert(
`Failed to load web3, accounts, or contract. Check console for details.`,
);
console.error(error);
}
};
render() {
if (!this.state.web3) {
return (
<h1>
Loading Web3, accounts and contract...
</h1>
)
}
return (
<div><h1>Address is: {this.state.account}</h1></div>
)
}
}
export default HomePage;
The content of getWeb3.js is the following:
import Web3 from "web3";
const getWeb3 = () =>
new Promise((resolve, reject) => {
// Wait for loading completion to avoid race conditions with web3 injection timing.
window.addEventListener("load", async () => {
// Modern dapp browsers...
if (window.ethereum) {
const web3 = new Web3(window.ethereum);
try {
// Request account access if needed
await window.ethereum.enable();
// Accounts now exposed
resolve(web3);
} catch (error) {
reject(error);
}
}
// Legacy dapp browsers...
else if (window.web3) {
// Use Mist/MetaMask's provider.
const web3 = window.web3;
console.log("Injected web3 detected.");
resolve(web3);
}
// Fallback to localhost; use dev console port by default...
else {
const provider = new Web3.providers.HttpProvider(
"http://127.0.0.1:8545"
);
const web3 = new Web3(provider);
console.log("No web3 instance injected, using Local web3.");
resolve(web3);
}
});
});
export default getWeb3;
Solution 1:[1]
This code seems to be wrong!
this.setState({ ContractInstance: instance, web3: web3, account: accounts[0] });
const owner = await this.state.ContractInstance.methods.getOwnerAddress().call();
You should not try to use state value as soon as you set up.
So you need to call your function in the second line like this:
const owner = await instance.methods.getOwnerAddress().call();
Injected connector code sample
const ConnectToInjected = async () => {
let provider = null;
if (typeof window.ethereum !== 'undefined') {
provider = window.ethereum;
try {
await provider.request({ method: 'eth_requestAccounts' })
} catch (error) {
throw new Error("User Rejected");
}
} else if (window.web3) {
provider = window.web3.currentProvider;
} else if (window.celo) {
provider = window.celo;
} else {
throw new Error("No Web3 Provider found");
}
return provider;
};
export default ConnectToInjected;
Usage:
const provider = await ConnectToInjected();
// Open metamask
await provider.request({ method: 'eth_requestAccounts' });
const web3 = new Web3(provider)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |