EIP-712 type hashing in Solidity tests
Hardhat provides three cheatcodes for EIP-712 hashing in Solidity tests:
The first two look up your project’s struct definitions by name, avoiding hand-written canonical type strings. eip712HashTypedData computes a digest from a self-describing JSON payload and works without any configuration.
Configuration
Section titled “Configuration”eip712HashType and eip712HashStruct need a registry of struct types to resolve names. Enable this by adding an eip712Types block to your hardhat.config.ts:
import { defineConfig } from "hardhat/config";
export default defineConfig({ test: { solidity: { eip712Types: { include: ["contracts/**", "test/contracts/**"], // exclude: ["contracts/legacy/**"], // Optional }, }, },});Note: To include structs from npm packages, match against the input source name used by solc (e.g.
"npm/@uniswap/permit2@*/src/interfaces/ISignatureTransfer.sol"). For the full reference, see Solidity tests configuration.
Example
Section titled “Example”If your bundled forge-std doesn’t expose these cheatcodes on its vm interface yet, declare a local interface and call them through the cheatcode address.
Given these struct definitions in contracts/Eip712Types.sol:
struct Person { address wallet; string name;}
struct Mail { Person from; Person to; string contents;}You can hash types and structs in your tests:
import "forge-std/Test.sol";import "../../contracts/Eip712Types.sol";
interface IEip712Cheats { function eip712HashType(string calldata) external pure returns (bytes32); function eip712HashStruct( string calldata, bytes calldata ) external pure returns (bytes32); function eip712HashTypedData(string calldata) external pure returns (bytes32);}
contract Eip712Test is Test { IEip712Cheats cheats = IEip712Cheats(address(vm));
function testMailTypeHash() public { bytes32 expected = keccak256( bytes( "Mail(Person from,Person to,string contents)" "Person(address wallet,string name)" ) ); assertEq(cheats.eip712HashType("Mail"), expected); }
function testMailStructHash() public { Mail memory m = Mail({ from: Person({ wallet: address(0xA), name: "Alice" }), to: Person({ wallet: address(0xB), name: "Bob" }), contents: "hello" });
bytes32 hash = cheats.eip712HashStruct("Mail", abi.encode(m)); // hash now equals the standard EIP-712 struct hash for `m`. }}Duplicate struct names
Section titled “Duplicate struct names”Each struct name must map to exactly one definition across the collected set. If two different structs share a name, the test run fails with error HHE818. To fix this, rename one of the structs or narrow your include/exclude globs. Identical definitions across multiple build infos are deduplicated silently.