gasLimit too low using Opensea Plugin

I am testing to send order to Opensea from an wallet with privatekey provided, but Is throw error:

Error place offer: at index: 1 Error: Error: Signer Error: Signer Error: gasLimit is too low. given 0, need at least 21944.
at signed (index.js:183)
at index.js:224

the code is below (this code works well if sending order from Metamask account):

const placeOffer = async () => {

    const { tokenAddress, amount, fromId, toId, duration } = tx;

    let web3 = await Moralis.Web3.enable();

   // below is a valid rinkeby account with enough ETH and WETH
    let botAcc = {address: .... , privateKey: ......};  

    await web3.eth.accounts.wallet.add(botAcc);
   
      try {

        await Moralis.Plugins.opensea.createBuyOrder({

          network: "testnet",

          tokenAddress: tokenAddress, //0xCae0b79679dC38E045525077Aa949aacBCB17791

          tokenId: "1",

          tokenType: "ERC721",

          amount: amount,

          userAddress: botAcc.address,

          paymentTokenAddress: "0xc778417e063141139fce010982780140aa0cd5ab",

        });

      } catch (er) {

        console.log("Error place offer: at index: 1 Error: ", er);

      }

  };

I think that MetaMask will set that gasLimit when is not set in the original request, what it happens if you set that gasLimit parameter in your request?

If calling order from account managed by Metamask, it set gasLimit, so it can go thru well. But what I am trying is to use separate account which is not managed by Metamask. I want the automated process without human approval thru Metamask.

I understand that part, you will have to set gasLimit parameter and also gas price parameter when you are not using MetaMask, I don’t know if it will work with the opensea plugin, but you should be able to send a transaction automatically using your private key if you set all the required parameters. In particular MetaMask makes a request to a node to find out a gasLimit estimation when that gas limit is not provided.

1 Like

I added gasLimit:22000 into parameters, but it dont works;
I dont think Moralis Plugin can have this parameter because in original OpenSea’s API, there is no such parameter for gasLimit either…
this not works:

try {

        await Moralis.Plugins.opensea.createBuyOrder({

          network: "testnet",

          tokenAddress: tokenAddress, //0xCae0b79679dC38E045525077Aa949aacBCB17791

          tokenId: i.toString(),

          tokenType: "ERC721",

          amount: amount,

          userAddress: botAcc.address, //user.get("ethAddress"), //"0x255b7DaBbBEa98dD315f8f6E09B3F2c5871dDf75",

          paymentTokenAddress: "0xc778417e063141139fce010982780140aa0cd5ab",

          expirationTime: expirationTime,

          gasLimit: 22000,

        });

      } catch (er) {

        console.log("Error place offer: at index:", i, " Error: ", er);

      }

@cryptokid @tungtien

This is something I can look into.
Might check tomorrow depending on workload.
Will keep you posted.

2 Likes

Hey @tungtien

Which web3 provider are you using to setup the project if not MetaMask?
Can you provide the part of your code where you setup the web3 provider?

I am using the Moralis React Ethereum boiler plate; then only added above code

Hi @tungtien,

The only transaction that can be (but not always is) triggered when calling createBuyOrder is the ERC721/1155 approval.

For the sake of testing I added the gas param to that call, so that you can use custom gas value if needed.

await Moralis.Plugins.opensea.createBuyOrder({
   network: '',
   tokenAddress: '',
   tokenId: '',
   amount: '',
   userAddress: '',
   tokenType: '',
   paymentTokenAddress: '',
   gas: 123 // Here your custom gas value
});

Let me know

Hi @dani,
I added gas,
at gas:22000 , it show error: transaction reverted: out of gas.
then when I changed gas to 50000, it shows this error:

inpage.js:1 MetaMask - RPC Error: Invalid parameters: must provide an Ethereum address. {code: -32602, message: ‘Invalid parameters: must provide an Ethereum address.’}

Error: {code: -32602, message: ‘Invalid parameters: must provide an Ethereum address.’}

my code is:

const placeOffer = async () => {

    const { tokenAddress, amount, fromId, toId, duration } = tx;

    const expirationTime = Math.round(Date.now() / 1000 + duration * 60 * 60);

    let web3 = await Moralis.Web3.enable();

    let botAcc = _testBot("rinkeby");

    await web3.eth.accounts.wallet.add(botAcc);

    try {

      await Moralis.Plugins.opensea.createBuyOrder({

        network: "testnet",

        tokenAddress: tokenAddress, //0xCae0b79679dC38E045525077Aa949aacBCB17791

        tokenId: "11",

        tokenType: "ERC721",

        amount: amount,

        userAddress: botAcc.address, 

        paymentTokenAddress: "0xc778417e063141139fce010982780140aa0cd5ab",

        expirationTime: expirationTime,

        gas: 50000,

      });

    } catch (er) {

      console.log("Error:", er);

    }

  };

Hey @tungtien

Regarding the errors:

  • at gas:22000 , it show error: transaction reverted: out of gas. This is because the gas you manually set was not enough to run the transaction.

  • Error: {code: -32602, message: ‘Invalid parameters: must provide an Ethereum address.’} This comes from your web3 instance. You are providing address x but the transaction is signed with address y.
    This is not related with the plugin itself.

Adding a custom gas seems to work as the gas param is correctly set.
Another solution might be not to run the transaction automatically but instead return and manually use the triggers from the plugin (something that might be discussed in the future).

How do I sign the transaction with the same x ?
Will this works if I call it from cloud function?

I suggest you to disable automatic triggers inspect / run them yourself.
In order to disabled the automatic execution of a plugin trigger you need to set the option object when you call the endpoint.
For example:

const createBuyOrder = async () => {
  try {
    const options = { disableTriggers: true }; // Option object
    const result = await Moralis.Plugins.opensea.createBuyOrder(
      {
        network: 'testnet',
        tokenAddress: '0xdf524d78dc60e185ba674eb64d04a8a45d228fba',
        tokenId: '421',
        amount: 0.0005,
        userAddress: web3Account.address,
        tokenType: 'ERC1155',
        paymentTokenAddress: '0xc778417e063141139fce010982780140aa0cd5ab',
        gas: 50000,
      },
      options // Option is passed as 2nd parameter when your call a plugin endpoint
    );
    console.log('Got parameters to create a new buy order::\n', result);
  } catch (error) {
    new Error(error.message || error);
  }
};

If you check the console, you will see that the result from the plugin is logged.

This is what the plugin sends back to the Moralis SDK.

Check the trigger array at position 0.

You see a web3Transaction trigger.

Now you can take code from the SDK and, after changing few thing, you can use your own account (instead of metamask) to run the triggers.

Moralis SDK: https://github.com/MoralisWeb3/Moralis-JS-SDK

I have done for you the web3Transaction trigger. You can play with the SDK to find out how to run new triggers such as web3Sign.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/web3.min.js"></script>
    <script src="https://unpkg.com/moralis/dist/moralis.js"></script>
  </head>
  <body>
    <script>
      const MORALIS_APP_ID = '';
      const MORALIS_SERVER_URL = '';
      const PK = '';
      const MORALIS_SPEEDY_NODE = '';

      // Connect to Moralis server
      const web3 = new Web3(MORALIS_SPEEDY_NODE);
      let web3Account;

      const init = async () => {
        await Moralis.start({ serverUrl: MORALIS_SERVER_URL, appId: MORALIS_APP_ID });
        await Moralis.enable();
        await Moralis.initPlugins();
        web3Account = await web3.eth.accounts.wallet.add(PK);
        createBuyOrder();
      };

      const createBuyOrder = async () => {
        try {
          const options = {
            disableTriggers: true,
          };
          const [user] = await web3.eth.getAccounts();
          const result = await Moralis.Plugins.opensea.createBuyOrder(
            {
              network: 'testnet',
              tokenAddress: '0xdf524d78dc60e185ba674eb64d04a8a45d228fba',
              tokenId: '421',
              amount: 0.0005,
              userAddress: web3Account.address,
              tokenType: 'ERC1155',
              paymentTokenAddress: '0xc778417e063141139fce010982780140aa0cd5ab',
              gas: 50000,
            },
            options
          );
          console.log('Got parameters to create a new buy order::\n', result);

          handleTriggers(result.triggers, result);
        } catch (error) {
          new Error(error.message || error);
        }
      };

      const ERROR_WEB3_MISSING =
        'Missing web3 instance, make sure to call Moralis.enableWeb3() or Moralis.authenticate()';

      const handleTriggers = async (triggersArray, payload) => {
        function ensureWeb3IsInstalled() {
          return web3 ? true : false;
        }
        if (!triggersArray) return;
        let response;
        for (let i = 0; i < triggersArray.length; i++) {
          switch (triggersArray[i]?.name) {
            case 'web3Transaction':
              if (!ensureWeb3IsInstalled()) throw new Error(ERROR_WEB3_MISSING);

              // Trigger a web3 transaction (await)
              if (triggersArray[i]?.shouldAwait === true)
                response = await web3.eth.sendTransaction(triggersArray[i]?.data);

              // Trigger a web3 transaction (does NOT await)
              if (triggersArray[i]?.shouldAwait === false) response = web3.eth.sendTransaction(triggersArray[i]?.data);

              // Return payload and response
              if (triggersArray[i]?.shouldReturnPayload === true) return { payload: payload, response: response };

              // Only return response
              if (triggersArray[i]?.shouldReturnResponse === true) return response;
              break;
            default:
              throw new Error(`Unknown trigger: "${triggersArray[i]?.name}"`);
          }
        }
      };
      init();
    </script>
  </body>
</html>

thanks @dani, this is big for me… I will study it and keep you informed on result…

1 Like

I am a little confused on this: don’t know from where to get this Web3 ?
can I just use:

web3 = await Moralis.Web3.enable();

because as I see during installing Opensea plugin we provided 2 Speedynodes url for both mainnet and testnet; and we appointed ‘testnet’ in the parameters:

Moralis.Plugins.opensea.createBuyOrder(
        {
          network: "testnet",...

another question:
inside the SDK: MoralisWeb3.js I see there is a line:

import { run } from './Cloud';

How do I import this {run} function into my code? I mean, is there a better way than just copy paste all related files from SDK into my folders?

Web3 should be already available in your browser from using a line like this in your html header:
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/web3.min.js"></script>

that run function can be accessible with Moralis.Cloud.run, and it will run a cloud function on your Moralis server

1 Like

Web3 should be already available in your browser from using a line like this in your html header:
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/web3.min.js"></script>

I am using React Moralis Boiler plate, so the Web3 is not yet available as global Class. So I ve just added: (hope it works?)
import Web3 from 'web3'

that run function can be accessible with Moralis.Cloud.run, and it will run a cloud function on your Moralis server

I dont think this run function (from MoralisSDK/scr/cloud.js) is the one we are calling by Moralis.Cloud.run:


export function run(name: string, data: mixed, options: RequestOptions): Promise<mixed> {
  options = options || {};

  if (typeof name !== 'string' || name.length === 0) {
    throw new TypeError('Cloud function name must be a string.');
  }

  const requestOptions = {};
  if (options.useMasterKey) {
    requestOptions.useMasterKey = options.useMasterKey;
  }
  if (options.sessionToken) {
    requestOptions.sessionToken = options.sessionToken;
  }
  if (options.context && typeof options.context === 'object') {
    requestOptions.context = options.context;
  }

Why do you need the run function?

To handle “callPluginEndpoint”, inside this fie, line 300 and 316 I found the function ‘run’

and I am trying to handle these triggers:

triggers: [
    {
      name: "web3Sign",
      message:
        "0xb5d36fbad51ea35a6b504fc94df0165a954d5d0ef7738c38369a1e4410c85d16",
      signer: "0x255b7dabbbea98dd315f8f6e09b3f2c5871ddf75",
      shouldAwait: true,
      saveResponse: true,
    },
    {
      name: "callPluginEndpoint",
      pluginName: "opensea",
      endpoint: "postOrder",
      params: {
        message:
          "0xb5d36fbad51ea35a6b504fc94df0165a954d5d0ef7738c38369a1e4410c85d16",
        orderSide: 0,
      },
      useSavedResponse: true,
      savedResponseAs: "signature",
      savedResponseAt: [],
      shouldAwait: true,
      runResponseTrigger: true,
    },
  ],

Consider this:

 {
      name: "callPluginEndpoint",
      pluginName: "opensea",
      endpoint: "postOrder",
      params: {
        message:
          "0xb5d36fbad51ea35a6b504fc94df0165a954d5d0ef7738c38369a1e4410c85d16",
        orderSide: 0,
      },
      useSavedResponse: true,
      savedResponseAs: "signature",
      savedResponseAt: [],
      shouldAwait: true,
      runResponseTrigger: true,
    },

You can call an endpoint in two ways:

  • Moralis.Plugins.opensea.postOrder()
  • POST req to https://coefdbgftezm.moralishost.com:2053/server/functions/opensea_createBuyOrder
    Replace: https://coefdbgftezm.moralishost.com:2053 with your server url