[SOLVED] .executeFunction() enters calls, but they remain stuck on pending

Hello friendly people!

So I’m trying to learn how to use Moralis by building a rather simplistic invoice generating dapp.

However when I try and execute a “write” transaction via Moralis.executeFunction(), metamask pops up and asks me to sign it, I do, Metamask closes (like its sending the Tx), then… nothing: when I check metamask to see the status, the transactions stay stuck permanently in pending mode. When I use the command line truffle instance to see if the transaction went through, the contract is empty of data. No errors are thrown (that I can see or know of).

I have tested this contract from the command line via truffle and it works flawlessly. I’m really perplexed because I read the Moralis docs on .executeFunction() multiple times, tried multiple different tweaks and I can’t figure out what is wrong.

Here’s my setup:
“moralis”: “^1.8.0”,
“react”: “^18.1.0”,
“react-dom”: “^18.1.0”,
“react-moralis”: “^1.4.0”,
“react-router-dom”: “^6.3.0”,
I set up Truffle and connected my local dev server to Moralis via FRP and double-checked the admin panel to verify that Moralis is connected to my local dev server instance. I also imported the dev accounts from truffle into my metamask.

I’m trying to write a .js file that exports a series of custom contract function calls, which will be imported into the [React] App.jsx file where I wrap those contract function calls with functions defined in react. The idea being that a button-click can trigger an async call to my contract and then will dump the result into App.jsx state variables, which then triggers downstream re-renders:

import Moralis from "moralis";
import Web3 from "web3";

let web3;

const ABI = [
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "uint256",
          "name": "id",
          "type": "uint256"
        },
        {
          "indexed": true,
          "internalType": "uint256",
          "name": "amount",
          "type": "uint256"
        },
        {
          "indexed": true,
          "internalType": "address",
          "name": "owner",
          "type": "address"
        }
      ],
      "name": "InvoiceCreated",
      "type": "event"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "amount",
          "type": "uint256"
        },
        {
          "internalType": "address",
          "name": "payee",
          "type": "address"
        },
        {
          "internalType": "address",
          "name": "payor",
          "type": "address"
        },
        {
          "internalType": "uint256",
          "name": "dueDate",
          "type": "uint256"
        },
        {
          "internalType": "string",
          "name": "memo",
          "type": "string"
        }
      ],
      "name": "create",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
]


const baseOptions = {
    contractAddress: "0x0eB611aAfc45E27cCc9c38BdE6F99457b1e38B94", 
    abi: ABI
};


export async function createInvoice(amount, payee, payor, dueDate, memo){

    let isWeb3Enabled = Moralis.isWeb3Enabled();

    if (!isWeb3Enabled) {
        await Moralis.enableWeb3({provider: "metamask"});
        web3 = new Web3(Moralis.provider);
    }
    else{
        web3 = new Web3(Moralis.provider);
    }

    
    const options = {
        functionName: "create",
        params: {
            amount: Moralis.Units.ETH(amount),
            payee: payee,
            payor: payor,
            dueDate: dueDate,
            memo: memo
        },
        ...baseOptions
    };

    const transaction = await Moralis.executeFunction(options);
    
    // Wait until the transaction is confirmed
    const receipt = await transaction.wait(1);

    return receipt.events[0].id; // return the invoice ID #
}

I’m really stuck and confused, any insight or help would be wonderful, thanks in advance!

Alright, after more research I figured out the problem(s):

  1. I was mixing vanilla style Moralis with React. React doesn’t play well with this because it is in control, so this means you need to make use of the “useWeb3ExecuteFunction” hook from the “react-moralis” library instead of trying to use the Moralis.executeFunction() method.
  2. I don’t know how much this played into my problems but you don’t include Moralis directly, instead its better to import it using the “useMoralis” hook, also from the “react-moralis” library.
  3. You must call Moralis.enableWeb3() before you can use the “useWeb3ExecuteFunction” hook, however you don’t need to use web3 itself directly inside of react.
  4. You get an instance of “useWeb3ExecuteFunction”, and that is an object that contains a “.fetch” call, which you use to pass your options object as params to the called contract function.

^ This video had all the answers for me, its made by the lovely folks at Moralis, and covers the exact situation I was dealing with, and its only about 15 minutes long, super easy to understand. After following this video I was able to fix my issues within 5 minutes.