0

I'm trying to link my smart contract to a boilerplate react frontend as follows:

import React, { useState, useEffect } from "react";
import MyContract from "./contracts/MyContract.json";
import getWeb3 from "./getWeb3";

const App = () => {
    const [value, setValue] = useState(undefined);
    const [web3, setWeb3] = useState(undefined);
    const [accounts, setAccounts] = useState([]);
    const [contract, setContract] = useState([]);

    useEffect(() => {
        const init = async () => {
            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 = MyContract.networks[networkId];
                const contract = new web3.eth.Contract(
                    MyContract.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.
                setWeb3(web3);
                setAccounts(accounts);
                setContract(contract);
                console.log(contract);
            } 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);
            }
        };
        init();
    }, []);

    useEffect(() => {
        const load = async () => {
            // Stores a given value, 5 by default.
            await contract.methods
                .mintToken("sample string")
                .send({ from: accounts[1] });

            // Get the value from the contract to prove it worked.
            const response = await contract.methods.ownerOf("1").call();
            setValue(response);
            // Update state with the result.
        };
        if (
            typeof web3 !== "undefined" &&
            typeof accounts !== "undefined" &&
            typeof contract !== "undefined"
        ) {
            load();
        }
    }, [web3, accounts, contract]);
    if (typeof web3 === "undefined") {
        return <div>Loading Web3, accounts, and contract...</div>;
    }
    return (
        <>
            <p>{value}</p>
        </>
    );
};

export default App;

I was using the truffle react box and tried to convert the old react syntax to useReact() and useEffect() hooks. When I run this I get the following errors: Uncaught (in promise) TypeError: contract.methods is undefined and Uncaught (in promise) Error: No "from" address specified in neither the given options, nor the default options.

But I console logged the contract object and the methods property has all the functions of the smart contract. How can I fix these errors and properly integrate my smart contract with the react frontend?

1 Answer 1

0

Your code might be throwing at that line :

const response = await contract.methods.ownerOf("1").call();

Uncaught (in promise) TypeError: contract.methods is undefined and Uncaught (in promise) Error: No "from" address specified in neither the given options, nor the default options.

You should specify the default sending account, or explicitly define the 'from' property (it is recommended to specify it explicitly).

const response = await contract.methods.ownerOf("1").call({from: accounts[1]});

EDIT : I wrote some code based on the truffle react box to reproduce your error. It would seem to be a race condition as I told you in the comments.

You need to guard against the second useEffect being triggered by the first one.

I added a new state variable :

 const [initialized, setInitialized] = useState(false);

Set it to true when initialization is done at the end of init:

setInitialized(true);

And only do the load logic if this state variable is true (ensuring that web3, accounts and contract are indeed initialized).

Removing this guard thows the same error as you :

Unhandled Rejection (TypeError): contract.methods is undefined

At this point this is more of a react / web3 issue than Ethereum, you will find the full code below.

import React, { useEffect, useState } from "react";
import SimpleStorageContract from "./contracts/SimpleStorage.json";
import getWeb3 from "./getWeb3";

import "./App.css";

const App = () => {
  const [storageValue, setStorageValue] = useState(0);
  const [web3, setWeb3] = useState(null);
  const [accounts, setAccounts] = useState(null);
  const [contract, setContract] = useState(null);
  const [initialized, setInitialized] = useState(false);

  const init = async () => {
    const _web3 = await getWeb3();
    const _accounts = await _web3.eth.getAccounts();
    const _networkId = await _web3.eth.net.getId();
    const _deployedNetwork = SimpleStorageContract.networks[_networkId];
    const _instance = new _web3.eth.Contract(
      SimpleStorageContract.abi,
      _deployedNetwork && _deployedNetwork.address
    );

    setWeb3(_web3);
    setAccounts(_accounts);
    setContract(_instance);
    setInitialized(true);
  };

  const load = async () => {
    if (initialized) {
      await contract.methods.set(5).send({ from: accounts[0] });
      const response = await contract.methods.get().call({from: accounts[0]});
      setStorageValue(response);
    }
  };

  useEffect(() => {
    init();
  }, []);

  useEffect(() => {
    load();
  }, [web3, accounts, contract]);

  if (!web3) {
    return <div>Loading Web3, accounts, and contract...</div>;
  }

  return (
    <div className="App">
      <h1>Good to Go!</h1>
      <p>Your Truffle Box is installed and ready.</p>
      <h2>Smart Contract Example</h2>
      <p>
        If your contracts compiled and migrated successfully, below will show a
        stored value of 5 (by default).
      </p>
      <p>
        Try changing the value stored on <strong>line 42</strong> of App.js.
      </p>
      <div>The stored value is: {storageValue}</div>
    </div>
  );
};

export default App;
4
  • This fixed the 2nd error. But "Unhandled Rejection (TypeError): contract.methods is undefined" this error is still there when trying to access the mintToken() function. (However, the transaction still goes through on metamask somehow despite the page not even loading) contract.methods has value inside the init() function but can't be accessed inside the load() function for some reason.
    – zerb
    Oct 30, 2021 at 19:24
  • You might be running into a race condition : Your first useEffect is basically the init of your component, the second one only listen to changes to web3, accounts and contract. Your call to setWeb3(web3) may trigger the second useEffect where you use contract while it's not yet initialized. Just an idea for now, I can check it properly later.
    – hroussille
    Oct 30, 2021 at 19:32
  • Also console.log(contract) inside the load() function returns two empty arrays and the contract object. Here's a screenshot of the console: ibb.co/c1KbVvP
    – zerb
    Oct 30, 2021 at 19:34
  • and this is for console.log(contract.methods): ibb.co/5jjYbGF
    – zerb
    Oct 30, 2021 at 19:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.