Search code examples
javascriptblockchainethereumsignatureethers.js

Invalid signature when siging EIP-712 data


I am trying call transferWithAuthorization on the USDC contract (which implements EIP-3009). To do this, I need to sign the transaction data and then execute the transaction, however I am getting the error: Fail with error 'FiatTokenV2: invalid signature'

Example of transaction error.

Any ideas why?

const { ethers } = require('ethers');
const abi = require('./abi');

async function main() {
  /* Generate Signature */
  const sendingWallet = ethers.Wallet.createRandom();
  const sendingAddress = await sendingWallet.getAddress();

  const types = {
    TransferWithAuthorization: [
      { name: 'from', type: 'address' },
      { name: 'to', type: 'address' },
      { name: 'value', type: 'uint256' },
      { name: 'validAfter', type: 'uint256' },
      { name: 'validBefore', type: 'uint256' },
      { name: 'nonce', type: 'bytes32' },
    ],
  };

  const domain = {
    name: 'testapp',
    version: '1',
    chainId: '5',
    verifyingContract: '0x07865c6E87B9F70255377e024ace6630C1Eaa37F', // USDC Contract
  };

  const message = {
    from: sendingAddress,
    to: '0x1bC152F3E47CC7baDF5629bc77CBEf9DaE813843', // Receiver wallet
    value: 100000,
    validAfter: 0,
    validBefore: Math.floor(Date.now() / 1000) + 3600, // Valid for an hour
    nonce: ethers.utils.hexValue(ethers.utils.randomBytes(32)), // 32 byte hex string
  };

  const signature = await sendingWallet._signTypedData(domain, types, message);

  console.log(signature);

  const { v, r, s } = ethers.utils.splitSignature(signature);

  console.log(v, r, s);


  /* Execute transaction */
  // Throw away wallet :)
  const wallet = ethers.Wallet.fromMnemonic(
    'youth tool amount venue exact birth opinion derive lend charge roof food',
  );

  const connection = new ethers.providers.InfuraProvider(
    'goerli', // or 'ropsten', 'rinkeby', 'kovan', 'goerli'
    'x', // Infura API Key
  );

  const signer = wallet.connect(connection);

  const contract = new ethers.Contract(
    '0x07865c6E87B9F70255377e024ace6630C1Eaa37F',
    abi,
    signer,
  );

  const res = await contract.transferWithAuthorization(
    message.from,
    message.to,
    message.value,
    message.validAfter,
    message.validBefore,
    message.nonce,
    v,
    r,
    s,
    {
      gasLimit: 1000000,
    },
  );

  console.log(res);

  const receipt = await res.wait();

  console.log(receipt);
}

main();


Solution

  • As of today (april 20th 2023) the domain separator for the USDC contract on goerli is 0x64b8869f66ce3d062d2e4b6b2819c6d5e2d3c7ebeb7025e8c819955f2c749012 with is the result of

    name: 'USD Coin',
    version: '2',
    chainId: 5,
    verifyingContract: '0x07865c6E87B9F70255377e024ace6630C1Eaa37F'
    

    The question uses an invalid name and version. That might be (part of) the problem.

    Note that since the contract is upgradeable, it is possible the domain separator was modified as part of a past upgrade.