ProviderError: The method eth_signTypedData_v4 does not exist/is not available

Hi @here, I was wrking on on workshop nft-drop by openzeppline. There the repository using --network localhost, For this it working fine. Here I want to use Kovan testnet for deploying, sign and redeem scripts for my usecase. When I use kova testnet for ( npx hardhat run scripts/2-sign.js --network kovan ) It gives me an error -> ProviderError: The method eth_signTypedData_v4 does not exist/is not available.
How to handle this.
Repository link : https://github.com/OpenZeppelin/workshops/tree/master/06-nft-merkle-drop

Hi @Waqas_Aslam. This is failing because the Kovan endpoint you're using doesn't manage any accounts, it just relays signed transactions. In other words, eth_signTypedData_v4 is asking to sign a message using a key, but the node doesn't have a key to sign with, otherwise it would be shared by everyone using the node.

You need to configure the Hardhat accounts for the Kovan network. Then Hardhat will intercept the eth_signTypedData_v4 request and sign using those local keys.

Hi @frangio Thanks for reply. Somehow I resolved this one by providing (INFURA provider) now I am facing another issue regards ESDSA.recover method. What i am facing now let me explain.

  1. I have deployed contracts for EIP712
  2. Another account using Private key is able to sign off chain transaction. It returns me a bytes32 signature.
  3. On chain verification for the signer returns me an invalid address of signerFor deployment

Can you share the code you're using?

When the verification returns the wrong address it generally means that the data was not signed properly. So please share the code that you used to generate the signature, and the Solidity code you're using to verify it.

Hi @frangio I am going to send you the code,

**For deployment on Kovan test net **

const { ethers }  = require('hardhat');
const minter = "0x9b171855086c77B74993bfB9D5dD5F9cafd21955";
const admin = process.env.ADMIN;

async function deploy(name, ...params) {
  const contractFactory = await ethers.getContractFactory(name);
  return await contractFactory.deploy(...params).then(f => f.deployed());
}

async function main() {
  const provider = new ethers.providers.InfuraProvider("kovan")
  const adminWallet = new ethers.Wallet(admin, provider);
  const adminAddress = adminWallet.connect(provider);
  console.log(`Deploying contracts:`);
  console.log(`- admin:   ${adminAddress.address} (${ethers.utils.formatEther(await adminAddress.getBalance())} ${ethers.constants.EtherSymbol})`);
  const registry = (await deploy('ERC721LazyMintWith712SignatureChecker', 'LazyMint','NFT')).connect(adminAddress);
  await registry.grantRole(await registry.MINTER_ROLE(), minter);

  console.log({ registry: registry.address });
}
main()
  .then(() => process.exit(0))
  .catch(error => {
    console.error(error);
    process.exit(1);
  });

For signing

const { ethers }  = require('hardhat');
const minter = process.env.MINTER;

async function attach(name, address) {
  const contractFactory = await ethers.getContractFactory(name);
  return contractFactory.attach(address);
}

async function main() {
const provider = new ethers.providers.InfuraProvider("kovan")
const MinterWallet = new ethers.Wallet(minter, provider);
const minterAddress = MinterWallet.connect(provider);
console.log(minterAddress.address);
console.log(`Sign authorization:`);

  const registry    = (await attach('ERC721LazyMintWith712SignatureChecker', process.env.ADDRESS)).connect(minterAddress);
  const { chainId } = await ethers.provider.getNetwork();
  const tokenId     = process.env.TOKENID || 1;
 
  const signature   = await minterAddress._signTypedData(
    // Domain
    {
      name: 'Name',
      version: '1.0.0',
      chainId,
      verifyingContract: registry.address,
    },
    // Types
    {
      NFT: [
        { name: 'tokenId', type: 'uint256' }
      ],
    },
    // Value
    { tokenId},
  );

  console.log({ registry: registry.address, tokenId, signature });
}

main()
  .then(() => process.exit(0))
  .catch(error => {
    console.error(error);
    process.exit(1);
  });

For on-chain verification

const { ethers }  = require('hardhat');
const relayer = process.env.RELAYER;
const minter = "0x9b171855086c77B74993bfB9D5dD5F9cafd21955";
const redeemer ="0xa54C70C6a41B2f77efB1a15d8b9522488404b026";

async function attach(name, address) {
  const contractFactory = await ethers.getContractFactory(name);
  return contractFactory.attach(address);
}

async function main() { 

  const provider = ethers.getDefaultProvider('kovan');
  const RelayerWallet = new ethers.Wallet(relayer, provider);
  const relayerAddress = RelayerWallet.connect(provider);

  
  console.log(`Collect token:`);

  const registry    = (await attach('ERC721LazyMintWith712SignatureChecker', process.env.ADDRESS)).connect(relayerAddress);
  const tokenId = 1;
  const signature   = process.env.SIGNATURE;
  
  const tx = await registry.collectNFT(redeemer, tokenId, minter, signature
    ,{
        gasPrice: 2000000000,
        gasLimit: 9000000,
    }
    );
  const receipt = await tx.wait();

  console.log(receipt);
}

main()
  .then(() => process.exit(0))
  .catch(error => {
    console.error(error);
    process.exit(1);
  });

Smart contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";

contract ERC721LazyMintWith712SignatureChecker is ERC721, EIP712, AccessControl {

    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    event TransferDone(address to, uint256 tokenId);
    constructor(string memory name, string memory symbol)
    ERC721(name, symbol)
    EIP712(name, "1.0.0")
    {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, AccessControl) returns (bool) {
        return super.supportsInterface(interfaceId);
    }

    function collectNFT(address to, uint256 tokenId, address signer, bytes calldata signature)
    external
    {
        require(_verify(signer, _hash(tokenId), signature), "Invalid signature");
        _safeMint(to, tokenId);
        emit TransferDone(to,tokenId);
        
    }

    function _hash(uint256 tokenId)
    internal view returns (bytes32)
    {
        return _hashTypedDataV4(keccak256(abi.encode(
            keccak256("NFT(uint256 tokenId)"),
            tokenId
        )));
    }

    function _verify(address signer, bytes32 digest, bytes memory signature)
    internal view returns (bool)
    {
        return hasRole(MINTER_ROLE, signer) && SignatureChecker.isValidSignatureNow(signer, digest, signature);
    }
}

Here I am using only tokenID (no struct) but in future I want to use a struct for NFT lazy minting, Now Please find where I am getting wrong,
**Also for deploying, signing, and on chain verification I am using --network kovan i.e

  1. npx hardhat run scripts/deployFactoryContracts.js --network kovan
  2. npx hardhat run scripts/signEIP712TypedData.js --network kovan
  3. npx hardhat run scripts/collectNFT.js --network kovan**

You are using a different name parameter in the deployment script ("LazyMint") and in the signing script ("Name"). It needs to be the same.

That value I ma using in constructor, so for the EIP712 domain it should same ?

Yes, because in your contract you initialize the EIP712 domain with the same name.

Okay let me change this one. Otherwise every thing is correct ?

I haven't checked every line, but I think it's ok.

Sure let me deploy again. If face any error will let you know.

Oh thats great @frangio Its working fine now. Thank you so much for helping me out

Also as currently i am using tokenID, now I have to use Structs for NFT so for them Is there anything to change in the sign scripts?

Please give it a shot and use the workshop code as reference. Let us know if you run into specific errors.

Is that repository can be used for commercial level?. As I am going to build my marketplace where I have to integrate this repo for lazy minting.

You can use the NFT Merkle Drop workshop for commercial purposes as it is MIT Licensed.

Thats perfect. from the security perspective is there any thing need to be changed by using this or not? As also I want to make these contracts upgradable so is it possible to make them upgradable? One more thins as Rarible using ERC1271 for signature validation So do I need to change this smart contract with ERC1271?

Once your code is finished you're going to have to get an expert opinion on the security of the code. At the moment I can't say whether anything needs to be changed.

Yes upgradeability is possible. Just give it a shot using our resources.

I'm not familiar with Rarible's use of ERC1271. You may need to change parts of the code and use our SignatureChecker library.

Thats great and appreciate your help. Now I am going to integrate it with my marketplace and will let you know if facing some errors. Thanks a lot again.

Hi @frangio Good day to you I ma going to implement ERC1155Upgradable, My concern about ERCI65Upgradable is that how can calculate the **SupportInterfaceId ** stated in ^0.8 version os solidity by Openzeppline i.e