i am using _signTypedData in etherjs to sign and encode a signature out of the data as follows
const domain = {
name: "og-nft",
version: "1",
};
const types = {
Nft: [
{ name: "URI", type: "string" },
{ name: "price", type: "uint256" },
],
};
// The data to sign
const [voucher, setVoucher] = useState({
URI: "",
price: '1',
});
const signature = await signer._signTypedData(domain, types, voucher);
I am storing the voucher and signature in the mongo database, I have deployed smart contract on hardhat and I am verifying the authenticity of signature by peering out the signer of the voucher using ECDSA.recover
function verifyVoucher(NFTVoucher calldata voucher, bytes memory signature)
public
view
returns (address)
{
require(voucher.price > 0, "Price must be greater than 0");
// require(voucher.tokenId > 0, "Token ID must be greater than 0");
bytes32 hash = _hash(voucher);
//string memory hash="";
return ECDSA.recover(hash, signature);
}
but the result of this is not matching with actual signer. i think I am making some mistake in the hash function above used.
0xe8c795f9168269940b31a470ad82e89a453e88b9 signer
0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 owner
below is the hash function.
function _hash(NFTVoucher calldata voucher)
internal
view
returns (bytes32)
{
return
_hashTypedDataV4(
keccak256(
abi.encode(
keccak256(
"Nft(string URI,uint256 price)"
),
keccak256(bytes(voucher.URI)),
voucher.price
)
)
);
}
Best Answer
You are missing 2 fields in your domain separator :
While they are not mandatory as per EIP-712, the draft-EIP712.sol that you seem to be using, is relying on them under the hood when computing the domain separator hash :
So to match draft-EIP712.sol computation you should include them in your domain separator on the client side to generate / sign the exact same data that your contract will verify :
where chainId is :
const { chainId, _ } = await provider.getNetwork();
and contract.address is simply the address of your deployed contract.I hope that answers your question.