Solidity - EIP-712 Typed Structured Data Hashing and Signing explains how smart contracts verify the signature of EIP-712. This article explains how to use Ethers.js for EIP-712 signing. This article uses Ethers.js version 6.7.1.
Sign signer.signTypedData Ethers.js uses the following function to sign. Refer to the previous article Using Ethers.js to Sign and Recover to obtain the Signer.
1 signer.signTypedData (domain, types, value): Promise <string>
domain: The data of eip712Domain in the contract. types: struct definition. value: Message content, Please refer to the example below directly. Return: A Promise object, and the content is a signature. Use the previous example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import "@openzeppelin/contracts/utils/cryptography/EIP712.sol" ;contract Example is EIP712 { struct Mail { address from ; address to; string contents; } bytes32 private constant MAIL_TYPEHASH = keccak256 ( "Mail(address from,address to,string contents)" ); constructor ( ) EIP712 ("Example" , "1" ) { } function hashStruct (Mail memory mail ) public pure returns (bytes32 ) { return keccak256 ( abi .encode ( MAIL_TYPEHASH, mail.from, mail.to, keccak256 (abi .encodePacked (mail.contents)) ) ); } function recover (Mail memory mail, uint8 v, bytes32 r, bytes32 s ) public view returns (address ) { bytes32 structHash = hashStruct(mail); bytes32 hash = _hashTypedDataV4(structHash); return ecrecover (hash , v, r, s); } }
The corresponding Ethers.js usage is as follows
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 const chainId = 1 ;const verifyingContract = '0xd8b934580fcE35a11B58C6D73aDeE468a2833fa8' ;const domain = { chainId, verifyingContract, name : 'Example' , version : '1' }; const types = { Mail : [ { name : 'from' , type : 'address' }, { name : 'to' , type : 'address' }, { name : 'contents' , type : 'string' } ] }; const value = { from : '0xBE0eB53F46cd790Cd13851d5EFf43D12404d33E8' , to : '0xDA9dfA130Df4dE4673b89022EE50ff26f6EA73Cf' , contents : 'Hello' }; const signature = await signer.signTypedData (domain, types, value);
We will get a not spiltted signature. We can refer to the previous article Using Ethers.js to Sign and Recover to handle it.
Other Examples struct array
1 2 3 4 5 6 7 8 9 10 struct Mail { Person from ; Person[] to; string contents; } struct Person { address wallet; string name; }
types and message change to the following
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 const types = { Mail : [ { name : 'from' , type : 'Person' }, { name : 'to' , type : 'Person[]' }, { name : 'contents' , type : 'string' } ], Person : [ { name : 'wallet' , type : 'address' }, { name : 'name' , type : 'string' } ] }; const value = { from : { wallet : '0xBE0eB53F46cd790Cd13851d5EFf43D12404d33E8' , name : 'Binance 7' }, to : [ { wallet : '0xDA9dfA130Df4dE4673b89022EE50ff26f6EA73Cf' , name : 'Kraken 13' } ], contents : 'Hello' };
Recover Signature If you want to recover and verify the signature in the backend for login purposes, you can also use the following functions:
1 verifyTypedData (domain, types, value, signature : SignatureLike ): string
domain: The data of eip712Domain in the contract. types: struct definition. value: Message content, Please refer to the example below directly. signature: Signature. It can be a not splitted string or an object containing vrs. Return: Signer address. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 import { verifyTypedData } from "ethers" ;const signature = '0x78c03866a5efa253157e62596194631b4c9757d38b3556994d6b879d79d713e666f9c0a3e21e48fd061c5cf5217fd7e3c3ced521aa39650fda179ce61e6763b11c' ;const address = verifyTypedData (domain, types, value, signature);
Further Reading Solidity - EIP-712 Typed Structured Data Hashing and Signing Using Web3.js to Sign and Recover EIP-712 Typed Structured Data Solidity - ecrecover Using Ethers.js to Sign and Recover