Want to transfer erc20 token from one account to another using meta transaction with help of erc2771

I want to transfer erc20 token from one account to another using meta transaction with help of erc2771.
I am using openzeppelin/contracts verison 5.0.1
I have two contracts

  1. MyToken - it inherit ERC2771Context
  2. Forwarder - it inherit ERC2771Forwarder
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/metatx/ERC2771Forwarder.sol";
import "hardhat/console.sol";

contract Forwarder is ERC2771Forwarder {

  constructor() ERC2771Forwarder("Forwarder") {}

  receive() payable external {}

below is js script:

  let iface = new ethers.Interface(ABI);
      var data =  iface.encodeFunctionData("transferToken", [ owner.address, bob.address, ethers.parseUnits("100", 18) ])

      let request = {
        from: bob.address,
        to: MyTokenAddress,
        value: 0,
        gasPrice: web3.utils.toHex(20 * 1e9),
        gas: web3.utils.toHex(20 * 1e9),
        nonce: web3.utils.toHex(web3.eth.getTransactionCount(gasPayer.address)),
        deadline: 1708583535,
        data: data,
      const signature =   await web3.eth.accounts.signTransaction(request, "private_key")
      request.signature = signature.rawTransaction;
   request.from = gasPayer.address

Here Bob sign transaction and gasPayer send transaction.
In above code I am getting signatureInvalid error.

what I am doing worng here?
is there any tutorial for erc2771 (want to deploy in private chain, can not use gelato, gsn or defender relayer)

There is no such function in the ERC20 Token Standard.

I made custom fuction to add some calculations

And are we supposed to guess what that function looks like?

BTW, you need to await on:

BTW2, your gas value looks way over the block gas limit:

Using such a high value will either delay your transaction for a very long time, or possibly even prevent its execution to begin with.

It is also pretty suspicious that you use the exact same value for both gas and gasPrice.

You should probably retrieve the gas value using something like:

const gas = await myTokenContract.transferToken(
    ethers.parseUnits("100", 18)

In addition to all of the above, you should make sure that all of the preliminary conditions required for this function to execute directly (i.e., not via the forwarder) are already in place.

For example, executing erc20TokenContract.transferFrom(x, y, z) using account w...
... requires executing erc20TokenContract.approve(w, z) using account x beforehand.

You haven't shared the details of your transferToken function, so it is hard to say whether or not any such preliminary conditions are required for executing this function.

Hey, I achive this functionality but still I am getting issue while sending meta transaction.

below is my input to execute function

    "from": "0x1D0b20965030a9b44B9604462b96D2B7881c01c5",
    "to": "0xDD468f4579721aaB4d3cDAdD82b2EE8234799BBF",
    "value": "0",
    "data": "0x2994cb500000000000000000000000001016c2c994ae243ab19aa6af1a1142bea23ca76a0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000065f9ae15000000000000000000000000000000000000000000000000000000000000001bed3b6987466caec08c2f51165759800f7641dff030076fa322fa7fb04271c0682e8bee8e07defa15c7f22b13cfb61dfcb380396cbdd1a13977c08fc4343fe81a",
    "gas": 1000000n,
    "deadline": 1710861845,
    "nonce": 23n,
    "signature": "0xa2a8695b4f03b172cee00be4d218cddd1d0a24a8ba59608dc7fb099dd74c04447b52b75560360f1b55990beb31426e8a618739f241d8f58883783273196f64991b"

data field contain below encodedData
var data1 = iface.encodeFunctionData("transferToken", [ signer.address, amount, 1710861845, v, r, s ])
where v, r, s comes from signature of erc20 token permit signature

if I verify my input data in forwarder contract then it return true but If I call execute with same data then it failed and return estimated_gas error
note: this is not happen continuously. sometime transaction complete succefully or sometimes it return error (even transaction not broadcast to network when error occure).

what cause this type of issue?
below is full code

  const types = { ForwardRequest };
  const typesPermit = { Permit };
  const domainForwarder = {
    name: 'Forwarder',
    version: '1',
    chainId: 80001,
    verifyingContract: Forwder_contract_Address,

  const domainPermit = {
    name: 'Token',
    version: '1',
    chainId: 80001,
    verifyingContract: TOKEN_CONTRACT_address

  const forgeRequest = async data => {
    const req = {
      from: bob.address,
      to: TOKEN_CONTRACT_address,
      value: 0,
      data: data,
      gas: 100000,
      deadline: 1710929073, // Expiry Time (current time + 2 hours)
      nonce: await ForwderContract.nonces(bob.address),
    req.signature = await bob.signTypedData(domainForwarder, types, req);

    return req;

  const forgeRequestPermit = async (owner, amount) => {
    const req = {
      owner: bob.address,
      spender: SPENDER_ADDRESS,
      value: amount,
      nonce: await tokenContract.nonces(bob.address),
      deadline:1710929073, // Expiry Time (current time + 2 hours),

    req.signature = await owner.signTypedData(domainPermit, typesPermit, req);
    return req;

function send() {
      const amount = ethers.parseUnits("1", 18)
      let sig = await forgeRequestPermit(bob, amount)
      const { v, r, s } = ethers.Signature.from(sig.signature); 
      var data1 =  iface.encodeFunctionData("fucntionname", [ signer.address, amount, 1710929073 (current time + 2 hours), v, r, s ])
      const request = await forgeRequest(data1);

      console.log(await ForwderContract.connect(signer).verify(request)) // print true
      const txn = await ForwderContract.connect(signer).execute(request) // return error