1 in 7 Defender transactions fails

Hi guys, thanks a lot for your work & on Defender. Really nice product!

What I need your help with: I have followed this guide: https://blog.openzeppelin.com/gasless-metatransactions-with-openzeppelin-defender/ & to put it shortly, I am trying to mint ERC-721 through a default MinimalForwarder.

The problem: sometimes approx. 1 in 7 txs are getting back invalid request. The rest work without a problem, and then sometimes they just dont.

:computer: Environment

I am using hardhat and calling the transactions from NodeJS

I am really lost on this, really appreciate any help or idea about what may be happening!

Code

const EIP712Domain = [
  { name: "name", type: "string" },
  { name: "version", type: "string" },
  { name: "chainId", type: "uint256" },
  { name: "verifyingContract", type: "address" },
];

const ForwardRequest = [
  { name: "from", type: "address" },
  { name: "to", type: "address" },
  { name: "value", type: "uint256" },
  { name: "gas", type: "uint256" },
  { name: "nonce", type: "uint256" },
  { name: "data", type: "bytes" },
];

function getMetaTxTypeData(chainId, verifyingContract) {
  return {
    types: {
      EIP712Domain,
      ForwardRequest,
    },
    domain: {
      name: "MinimalForwarder",
      version: "0.0.1",
      chainId,
      verifyingContract,
    },
    primaryType: "ForwardRequest",
  };
}

task("sign", "Sign meta-tx").setAction(async (taskArgs, hre) => { 
async function buildRequest(forwarder, input) {
      const nonce = await forwarder
        .getNonce(input.from)
        .then((nonce) => nonce.toString());
      return { value: 0, gas: 1e6, nonce, ...input };
    }

    async function buildTypedData(forwarder, request) {
      const chainId = await forwarder.provider
        .getNetwork()
        .then((n) => n.chainId);
      const typeData = getMetaTxTypeData(chainId, forwarder.address);
      return { ...typeData, message: request };
    }

    async function signTypedData(signer, from, data) {
      // If signer is a private key, use it to sign
      if (typeof signer === "string") {
        const privateKey = Buffer.from(signer.replace(/^0x/, ""), "hex");
        return signTypedMessage(privateKey, { data });
      }
    }

    async function signMetaTxRequest(signer, forwarder, input) {
      const request = await buildRequest(forwarder, input);
      const toSign = await buildTypedData(forwarder, request);
      const signature = await signTypedData(signer, input.from, toSign);
      return { signature, request };
    }

    function getInstance(name, address) {
      return hre.ethers.getContractFactory(name).then((f) => f.attach(address));
    }

    console.log("Signing...");

    const forwarder = await getInstance("MinimalForwarder", ForwarderAddress);
    const contractFactory = await getInstance(
      "ContractFactory",
      "string data"
    );

    const from = new hre.ethers.Wallet(taskArgs.privatekey).address;

    const data = contractFactory.interface.encodeFunctionData("mintNFT", [
      "string data",
      "string data",
    ]);

 
    const result = await signMetaTxRequest(taskArgs.privatekey, forwarder, {
      to: contractFactory.address,
      from,
      data,
    });

    const { request, signature } = result;

    // Initialize Relayer provider and signer, and forwarder contract
    const credentials = {
      apiKey: "###",
      apiSecret: "###",
    };
    const provider = new DefenderRelayProvider(credentials);
    const signer = new DefenderRelaySigner(credentials, provider, {
      speed: "average",
    });
    const forwarderContract = new hre.ethers.Contract(
      ForwarderAddress,
      ForwarderAbi,
      signer
    );

    // Validate request on the forwarder contract
    console.log("Verifying... ");
    const valid = await forwarderContract.verify(request, signature);
    if (!valid) throw new Error("Invalid request");
    console.log("TX valid... ");

    // Relay transaction!
    const gasLimit = (parseInt(request.gas) + 50000).toString();
    const tx = await forwarderContract.execute(request, signature, {
      gasLimit,
    });
    // const tx = await relay(forwarder, request, signature);
    console.log(`Sent meta-tx: ${tx.hash}`);
    return { txHash: tx.hash };
  });

Hey @cris-co,
Thanks for reaching out. I'm happy you love Defender!

Can you send me via DM your tenant id and the id of the relayer? I will check what's the reason behind the invalid request.

Thanks

Hi @Aleksandr, i cant find how to send DMs... :sweat_smile:

No worries, I've sent you one. You should get a notification :wink: