How can i handle this error - Moralis Moralis.enableWeb3() already has been called, but is not finished yet

I getting this error, when calling multiple function at same timeā€¦ some time if once executed code is not completedā€¦ How can i handle this?
If i am trying to use try catch - i am getting this error
Error: Missing web3 instance, make sure to call Moralis.enableWeb3() or Moralis.authenticate()

As mentioned, make sure you call Moralis.enableWeb3() or with an authenticated user authenticating Moralis.authenticate() .

Do you have code snippet of where you are calling enableWeb3?

Hi i have created a class where i am writing all web3 execution code that named web3Services.js, and another page index.js in click item i am calling one of my function Buy itemā€¦
so my web3Services Code is like thisā€¦remember this is master codeā€¦i want my developer call from hereā€¦

case :- sometime ā€¦user is already connected their wallet with my app and sometime they are not connectedā€¦ and trying to call buy function directlyā€¦so i want that time connect first and execute next code,ā€¦

import { ethers } from "ethers";

export const Moralis = require('moralis');

/*
* You can write all web3 based call here
* so, no need to rewrite in every page, like mint function
*/
export class Web3Service {

    /*
    // Better practice to use this thing from .env, find solution
    CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS
    SIGNER_KEY = process.env.PRIVATE_KEY
    CHAIN_ID = process.env.CHAIN_ID
    */
    CONTRACT_ADDRESS = "XXXXXX"
    PRIVATE_KEY = "XXXXXX"
    CHAIN_ID = 4
    CHAIN_HASH = "0x4"

    //Connected User Data

    getWei = async (price) => {
        return Moralis.Units.ETH(price, 18)
    }

    enableWeb3 = async () => {
        try{
            await Moralis.enableWeb3(); 
        } catch(err){
            console.error("Error enableWeb3", err)
        }
    }

    //check if web3 is enable, either enable it
    //Call this function always
    isWeb3Enable = async () => {
        if(Moralis.isWeb3Enabled() == false) {
            try{
                await Moralis.enableWeb3();
            } catch(err){
                console.error("Error isWeb3Enable", err)
            }
        }
    }

    //return connected user and chain id
    getWallet = async (providerName="metamask", verifyChain=false) => {
        try {
            if(verifyChain){
                await this.verifyChain()
            }
            const web3Provider = await Moralis.enableWeb3({ provider: providerName });
            if(web3Provider.provider.constructor.name === "WalletConnectProvider"){
                return [web3Provider.provider.accounts[0], ethers.utils.hexValue(web3Provider.provider.chainId), providerName]
            }
            return [web3Provider.provider.selectedAddress, web3Provider.provider.chainId, providerName]
        } catch(err) {
            console.error("Error getWallet: Web3 Initialization Execption", err)
            return []
        }
    }

    //return web3Provider 
    getChain = async () => {
        try {
            const chainId = await Moralis.chainId;
            return chainId
        } catch(err) {
            console.error("Error getChain: Web3 Initialization Execption", err)
            return null
        }
    }

    verifyChain = async () => {
        const chainId = await Moralis.chainId;
        if(parseInt(chainId)!=this.CHAIN_ID){
            await this.switchChain(this.CHAIN_HASH)
        }
    }

    //return web3Provider 
    switchChain = async (chainId) => {
        try {
            await Moralis.switchNetwork(chainId);
            return chainId
        } catch(err) {
            console.error("Error switchChain: Web3 Initialization Execption", err)
            return chainId
        }
    }

    //execute when user change acccount on metamask
    metamaskAccountChange = async () => {
        ethereum.on('accountsChanged', (accounts) => {
            return accounts
        });
        return null
    }

    //execute when user change chain on metamask
    metamaskChainChange = async () => {
        ethereum.on('chainChanged', (chain) => {
            return chain
        });
        return null
    }

    isMetamaskUnlocked = async () => {
        ethereum._metamask.isUnlocked().then((res)=> {
            console.log("is metamask connected: ", res)
            return res
        })
    }

    /*
    *   Return current gas price 
    */
    getGasPrice = async (provider="metamask") => {
        const web3Provider = await Moralis.enableWeb3({ provider: provider });
        return await web3Provider.getGasPrice();
    }

    //function to check if contract is approved to access token
    // You can modify condition as per your need
    isApprovedForAll = async (account, operator, provider = "metamask") => {

        const ABI =  [{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

        const readOptions = {
            contractAddress: this.CONTRACT_ADDRESS,
            chainId: this.CHAIN_ID,
            functionName: "isApprovedForAll",
            abi: ABI,
            params: {
                account: account, 
                operator: operator
            }
        };
        await this.enableWeb3()
        return await Moralis.executeFunction(readOptions);
    }

    //function to approve to access token
    // You can modify condition as per your need
    // if operator address is constant, no need to pass in argument, but declare here
    setApprovalForAll = async (account, operator, approved = true, providerName = "metamask") => {

        if(!this.isApprovedForAll(account, operator)){
            const ABI =  [{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"}]

            const writeOptions = {
                contractAddress: this.CONTRACT_ADDRESS,
                chainId: this.CHAIN_ID,
                functionName: "setApprovalForAll",
                abi: ABI,
                params: {
                    operator: operator, 
                    approved: approved
                }
            };
            const receipt  = await Moralis.executeFunction(writeOptions)
            await receipt.wait()
            return receipt
        } else {
            return "already aproved"
        }
    }

    getTokenNo = async (creator, amount, nonce, collectionId) => {
        //first verify .. selected chain is as above define
        const readOptions = {
            contractAddress: this.CONTRACT_ADDRESS,
            functionName: "genTokenHash",
            abi: [{"inputs":[{"internalType":"address","name":"_creator","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_nonce","type":"uint256"},{"internalType":"string","name":"_collectionId","type":"string"}],"name":"genTokenHash","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}],
            params: {
                _creator: creator,
                _amount: amount,
                _nonce: nonce,
                _collectionId: collectionId
            }
        };
        await this.enableWeb3()
        const message = await Moralis.executeFunction(readOptions);
        //console.log(message, "Token Details")
        return message
    }

    //function to approve to access token
    // You can modify condition as per your need
    // if operator address is constant, no need to pass in argument, but declare here
    buy = async (tokenNo, price, amount, buyer, seller, paytoken, nonce, tuple, trade_type, providerName = "metamask") => {
        try{
            const ABI = [{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_buyer","type":"address"},{"internalType":"address payable","name":"_seller","type":"address"},{"internalType":"address","name":"_pToken","type":"address"},{"internalType":"uint256","name":"_nonce","type":"uint256"},{"components":[{"internalType":"string","name":"tokenURI","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"collectionId","type":"string"},{"internalType":"address","name":"creator","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"uint256","name":"blockTime","type":"uint256"}],"internalType":"struct Marketplace.Token","name":"_token","type":"tuple"},{"internalType":"uint8","name":"_tradeType","type":"uint8"}],"name":"buyAndAccept","outputs":[],"stateMutability":"payable","type":"function"}];

            const writeOptions = {
                contractAddress: this.CONTRACT_ADDRESS,
                functionName: "buyAndAccept",
                abi: ABI,
                params: {
                    _tokenId: tokenNo,
                    _price: price,
                    _amount: amount,
                    _buyer: buyer,
                    _seller: seller,
                    _pToken: paytoken,
                    _nonce: nonce, //read from db
                    _token: tuple,
                    _tradeType: trade_type
                },
                msgValue: paytoken != "0x0000000000000000000000000000000000000000"? 0: price * amount
            };
            await this.enableWeb3()
            const tx = await Moralis.executeFunction(writeOptions);
            console.log("Before wait: ", tx)
            await tx.wait();
            return [true, tx ]
        } catch(err) {
            return [false, err ]
        }
    }
}

My index.html code is like this

const buy = async () => {

    //always call this first
    //await web3Service.enableWeb3()
    await connectWallet() //check here ..first trying to login

    const paytoken = "0x0000000000000000000000000000000000000000"
    const trade_type = 0
    const price = await web3Service.getWei(0.001) //check here
    const amount = 4
    const seller = "xxxxxxxxxxxxxxxxxxx"
    const buyer = wallet
    const collectionId = "xxxxxxxxxxxxxxxxxx"
    const nonce = new Date().getTime()
    const tokenArr = await web3Service.getTokenNo(seller, amount, nonce, collectionId) //chjeck here
    const tokenBlock = tokenArr[0]
    const tokenTime = tokenArr[1]
    const tokenNo = tokenArr[2]
    const tuple = [
      "https://gateway.ipfs.io/ipfs/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      amount,
      collectionId,
      seller,
      tokenBlock,
      tokenTime
    ]

    web3Service.buy(tokenNo, price, amount, buyer, seller, paytoken, nonce, tuple, trade_type).then((res)=> {
      if(res[0]){
        console.log("transaction success", res[1])
        setMsg(res[1])
      } else {
        console.error("transaction failed", res[1])
        setMsg(res[1])
      }
    }).catch((err)=>{
      console.error(err)
      setMsg(err)
    })

  }

and login code is in index.html

const connectWallet = async () => {
    await web3Service.enableWeb3()
    web3Service.getWallet(selectedProvider).then(async (connection) => {
      if(connection.length > 0){
        setWallet(connection[0])
        setChain(connection[1])
        setProvider(connection[2])
        setMsg(`Connect wallet address is ${connection[0]} & chain id ${connection[1]} & provider is ${connection[2]}`)
      }
    })
  }

and this my useEffect

useEffect(() => { 
    //code to change account if 
    web3Service.metamaskAccountChange().then((res)=>{ setWallet(res) })
    web3Service.metamaskChainChange().then((res)=>{ setChain(res) })
    web3Service.isMetamaskUnlocked().then((res)=> {
      if(provider === "metamask" && res === false){
        disconnect()
      }
    });
  }, [])

how can i manage code , so my frontend team can call these function directly on their codeā€¦instead of wrting same thing everytimeā€¦ i am getting these error - first
Error enableWeb3 Error: Cannot execute Moralis.enableWeb3(), as Moralis Moralis.enableWeb3() already has been called, but is not finished yet
second in same call
Uncaught (in promise) Error: Missing web3 instance, make sure to call Moralis.enableWeb3() or Moralis.authenticate()
if i have already calledā€¦why is it asking for again execute that function???

You can check if web3 is enabled with Moralis.isWeb3Enabled() before you enableWeb3

can you make a minimal code example that replicates this error?

I donā€™t know how to consistently replicate it

Minimal code, Okay check this
in index.js, i have written this on page load

step 1 - in page load

//some required code in page laod 
  useEffect(() => { 
    //code to change account if 
    web3Service.isWeb3Enable(); //here i have enable web3 instance
    web3Service.metamaskAccountChange().then((res)=>{ setWallet(res) }) //this contains metamask docs code
    web3Service.metamaskChainChange().then((res)=>{ setChain(res) }) //this contains metamask docs code
    web3Service.isMetamaskUnlocked().then((res)=> {
      if(provider === "metamask" && res === false){
        disconnect()
      }
    });
  }, [])

Now my isWeb3Enable code is

//check if web3 is enable, either enable it
    //Call this function always
    isWeb3Enable = async () => {
        if(Moralis.isWeb3Enabled() == false) {
            try{
                await this.enableWeb3()
            } catch(err){
                console.error("Error isWeb3Enable", err)
            }
        }
    }

Now i am calling buy functionā€¦which execute

/*
  * Here need to read data from database
  */
  const buy = async () => {

    //verify selected chain must be 0x4 in live 0x1
    await web3Service.verifyChain()
    //always call this first
    //await web3Service.enableWeb3()
    await connectWallet() //because i need connected user... below called buyer, useState wallet, so i will login
    //write as per your need
    const paytoken = "0x0000000000000000000000000000000000000000"
    const trade_type = 0
    const price = await web3Service.getWei(0.001)
    const amount = 4
    const seller = "0x00000000000000000000000000000000"
    const buyer = wallet //value set on connectWallet
    const collectionId = "00000000000000000000"
    const nonce = new Date().getTime()
    const tokenArr = await web3Service.getTokenNo(seller, amount, nonce, collectionId)
    const tokenBlock = tokenArr[0]
    const tokenTime = tokenArr[1]
    const tokenNo = tokenArr[2]
    const tuple = [
      "https://gateway.ipfs.io/ipfs/00000000000000000000000000",
      amount,
      collectionId,
      seller,
      tokenBlock,
      tokenTime
    ]

    web3Service.buy(tokenNo, price, amount, buyer, seller, paytoken, nonce, tuple, trade_type).then((res)=> {
      if(res[0]){
        console.log("transaction success", res[1])
        setMsg(`Success transaction no. is ${res[1]}}`)
      } else {
        console.error("transaction failed", res[1])
        setMsg('Transaction is failed, retry.')
      }
    }).catch((err)=>{
      console.error(err)
      setMsg(err)
    })

  }

i need this connect wallet to verify or read connected user wallet addressā€¦ this line ( await connectWallet()), inside this function, i am saving setWallet(ā€œit returned wallet addressā€)ā€¦this address i have to pass to buyer variable aboveā€¦

Step 2: web3Service.js

enableWeb3 = async () => {
        await Moralis.enableWeb3(); 
    }

    //check if web3 is enable, either enable it
    //Call this function always
    isWeb3Enable = async () => {
        if(Moralis.isWeb3Enabled() == false) {
            try{
                await this.enableWeb3()
            } catch(err){
                console.error("Error isWeb3Enable", err)
            }
        }
    }

//to get connected user
//return connected user and chain id
    getWallet = async (providerName="metamask", verifyChain=false) => {
        try {
            if(verifyChain){
                await this.verifyChain()
            }
            const web3Provider = await Moralis.enableWeb3({ provider: providerName });
            if(web3Provider.provider.constructor.name === "WalletConnectProvider"){
                return [web3Provider.provider.accounts[0], ethers.utils.hexValue(web3Provider.provider.chainId), providerName]
            }
            return [web3Provider.provider.selectedAddress, web3Provider.provider.chainId, providerName]
        } catch(err) {
            console.error("Error getWallet: Web3 Initialization Execption", err)
            return []
        }
    }


    verifyChain = async () => {
        const chainId = await Moralis.chainId;
        if(parseInt(chainId)!=this.CHAIN_ID){
            await this.switchChain(this.CHAIN_HASH)
        }
    }

//return web3Provider 
    switchChain = async (chainId) => {
        try {
            await Moralis.switchNetwork(chainId);
            await this.enableWeb3() //need to call due to relogin after network change
            return chainId
        } catch(err) {
            console.error("Error switchChain: Web3 Initialization Execption", err)
            return chainId
        }
    }

//execute when user change acccount on metamask
    metamaskAccountChange = async () => {
        ethereum.on('accountsChanged', (accounts) => {
            return accounts
        });
        return null
    }

    //execute when user change chain on metamask
    metamaskChainChange = async () => {
        ethereum.on('chainChanged', (chain) => {
            return chain
        });
        return null
    }

    isMetamaskUnlocked = async () => {
        ethereum._metamask.isUnlocked().then((res)=> {
            console.log("is metamask connected: ", res)
            return res
        })
    }
getTokenNo = async (creator, amount, nonce, collectionId) => {
        //first verify .. selected chain is as above define
        const readOptions = {
            contractAddress: this.CONTRACT_ADDRESS,
            functionName: "genTokenHash",
            abi: [{"inputs":[{"internalType":"address","name":"_creator","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_nonce","type":"uint256"},{"internalType":"string","name":"_collectionId","type":"string"}],"name":"genTokenHash","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}],
            params: {
                _creator: creator,
                _amount: amount,
                _nonce: nonce,
                _collectionId: collectionId
            }
        };
        const message = await Moralis.executeFunction(readOptions);
        //console.log(message, "Token Details")
        return message
    }
//function to approve to access token
    // You can modify condition as per your need
    // if operator address is constant, no need to pass in argument, but declare here
    buy = async (tokenNo, price, amount, buyer, seller, paytoken, nonce, tuple, trade_type, providerName = "metamask") => {
        try{
            const ABI = [{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_buyer","type":"address"},{"internalType":"address payable","name":"_seller","type":"address"},{"internalType":"address","name":"_pToken","type":"address"},{"internalType":"uint256","name":"_nonce","type":"uint256"},{"components":[{"internalType":"string","name":"tokenURI","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"collectionId","type":"string"},{"internalType":"address","name":"creator","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"uint256","name":"blockTime","type":"uint256"}],"internalType":"struct Marketplace.Token","name":"_token","type":"tuple"},{"internalType":"uint8","name":"_tradeType","type":"uint8"}],"name":"buyAndAccept","outputs":[],"stateMutability":"payable","type":"function"}];

            const writeOptions = {
                contractAddress: this.CONTRACT_ADDRESS,
                functionName: "buyAndAccept",
                abi: ABI,
                params: {
                    _tokenId: tokenNo,
                    _price: price,
                    _amount: amount,
                    _buyer: buyer,
                    _seller: seller,
                    _pToken: paytoken,
                    _nonce: nonce, //read from db
                    _token: tuple,
                    _tradeType: trade_type
                },
                msgValue: paytoken != "0x0000000000000000000000000000000000000000"? 0: price * amount
            };
            const tx = await Moralis.executeFunction(writeOptions);
            console.log("Before wait: ", tx)
            await tx.wait();
            return [true, tx.hash ]
        } catch(err) {
            return [false, err ]
        }
    }

i have removed enableWeb3ā€¦so my first error in page loadā€¦
Error isWeb3Enable Error: Cannot execute Moralis.enableWeb3(), as Moralis Moralis.enableWeb3() already has been called, but is not finished yet
same on buy also

in useEffect, ihave not used await withy web3Service.isWeb3Enable();ā€¦becauseā€¦i dont want to wait but run this on backgroundā€¦ am i bad in react programming?..i have never faced this issues on angular and vanilla jsā€¦
also i want to update hereā€¦sometime buy function is workingā€¦but if i will refresh and again execute buy,i will get enable web3 require based errorā€¦you can see in login i am running web3ā€¦

minimal codeā€¦if i have to execute one function onlyā€¦its working fineā€¦
issue is only when i need to relogin or if i am calling web3Enable again as per requirements ā€¦if i will write this code in one place so i can use this on every projectsā€¦what in erc721 and erc1155 ā€¦there is onlyā€¦mint, transfer, approve is mostly used functionā€¦ need to change only abi in every project

I think that this was solved in discord (by not making so many calls to enableWeb3 at the same time), do you still have issues?

Hi, I checkedā€¦if i am writing enable web3 in page loadā€¦it is working fineā€¦but need to be write once onlyā€¦so now in my codeā€¦in use effectā€¦i am writing this enable web3 ā€¦and then only running other codesā€¦

Sorry i am waiting for issue if again i will faceā€¦ bcz i have already done code like thisā€¦but i had faced no web3 enable issue ā€¦ i am just checking when i had faced that errorā€¦

it works fine now for you?

yesā€¦its okay fr now

1 Like