Meta Transactions - Forwarder.verify returns false

Hello everyone; I'm reposting this question to make sure it's not confusing.

I'm preparing a EIP712 signature request to be signed by user:

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

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

  const forwarderContract = new ethers.Contract('0x51C3354aC8143952Db46137947ED318299191803', ForwarderABI, provider);
  const nonce = getNonce.toString(); 
  const chainId = await forwarderContract.provider.getNetwork().then(n => n.chainId);
  const data = unsignedProgram.interface.encodeFunctionData('leaveChallenge', [blockchainAddress]);
  const getGas = await provider.getGasPrice();
  const gas = getGas.toString();

  const request = {
    from: '0xB55A8bb04aF693823B0A00bF300b67a85BED6282',
    to: '0x7e372916CC0d6EFc43c59637F804382d46Cc894a',
    value: 0,
    gas: gas,
    nonce,
    data
  };

  const TypedData = {
    domain: {
      name: 'MinimalForwarder',
      version: '0.0.1',
      chainId: chainId,
      verifyingContract: '0x51C3354aC8143952Db46137947ED318299191803',
    },
    primaryType: 'ForwardRequest',
    types: {
      EIP712Domain: EIP712DomainType,
      ForwardRequest: ForwardRequestType
    },
  };

  const toSign = { ...TypedData, message: request };

At this point the signature request is ready. I sign the transaction and I get back this signature HASH 0xed12ec9be159fae7461b85aa84d5c9cb707c8f85295d4d71ea3410daeee1272e7d994f3228022a4587b8c3f73478ed3b1e5c7f2c0aab2081fc0f9f025fad18721b.

I then prepare the forwarderContract.verify contract call.

   const blockNumber = await provider.getBlockNumber();
   const block = await provider.getBlock(blockNumber);
   const timestamp = block.timestamp;
 
   const requestData = {
       from: serializedTransaction.message.from,
       to: serializedTransaction.message.to,
       value: serializedTransaction.message.value,
       gas: serializedTransaction.message.gas,
       deadline: timestamp + 600,
       data: serializedTransaction.message.data,
       signature: signature
   };
// requestData Values ^^ 
{
 from: '0xB55A8bb04aF693823B0A00bF300b67a85BED6282',
 to: '0x7e372916CC0d6EFc43c59637F804382d46Cc894a',
 value: 0,
 gas: '25000000000',
 deadline: 1706045929,
 data: '0xb4477c0147daecad546ef3fb323f46318050e87d0ee5144eb127e953e181dc59d409a762',
 signature: '0xed12ec9be159fae7461b85aa84d5c9cb707c8f85295d4d71ea3410daeee1272e7d994f3228022a4587b8c3f73478ed3b1e5c7f2c0aab2081fc0f9f025fad18721b'
}
//
   const forwarderContract = new ethers.Contract('0x51C3354aC8143952Db46137947ED318299191803', ForwarderABI, provider);
   const verify = await forwarderContract.verify(requestData);
   console.log(verify);

I'm not sure why I get back false; Checking the docs for verify: I mad sure my deployed forwarder contract is trusted in my target contract and the requestData from value matches the user address who signed and deadline is not yet expired:

A transaction is considered valid when the target trusts this forwarder, the request hasn’t expired (deadline is not met), and the signer matches the from parameter of the signed request.

@ernestognw @barakman @ericglau @abcoathup

I tried adding delay to be part of the ForwardRequestType. But had no luck :slight_smile:

    { name: 'from', type: 'address' },
    { name: 'to', type: 'address' },
    { name: 'value', type: 'uint256' },
    { name: 'gas', type: 'uint256' },
    { name: 'nonce', type: 'uint256' },
    { name: 'data', type: 'bytes' },
    { name: 'delay', type: 'uint48' },
  ];

UPDATE:

I'm able to recover address using:

const recovery = ethers.verifyTypedData(TypedData.domain, TypedData.types, TypedData.message, signature);

// recovery = '0xB55A8bb04aF693823B0A00bF300b67a85BED6282'

However verify still returns false!

I finally found the issue I was encountering; I passed the wrong nonce value so _hashTypedDataV4((...)).tryRecover(request.signature);

I was assigning the overall nonce value of the signer instead of the wallet forwarder transaction count.

// Correct way
      const getNonce = await forwarderContract.nonces(wallet.address!);
      const nonce = getNonce.toString();

The forwarder contract verifies your forward request signature using the nonces function inherited from the Nonces contract to protect from replay attacks.